【问题标题】:How do you convert char numbers to decimal and back or convert ASCII 'A'-'Z'/'a'-'z' to letter offsets 0 for 'A'/'a' ...?如何将 char 数字转换为十进制并返回或将 ASCII 'A'-'Z'/'a'-'z' 转换为 'A'/'a' 的字母偏移量 0 ...?
【发布时间】:2013-12-11 20:27:14
【问题描述】:

如果你有一个 '0' 到 '9' 范围内的字符,你如何将它转换为 0 到 9 的 int 值

然后你怎么把它转换回来?

还给定字母“A”到“Z”或“a”到“z”,如何将它们转换为 0-25 范围然后返回?

可以针对 ASCII 进行优化

【问题讨论】:

    标签: c++ c ascii


    【解决方案1】:

    C++ 指定的基本 char 编码使 '0' - '9' 之间的转换变得容易。

    C++ 规定:

    在源基本字符集和执行基本字符集中,上述十进制数字列表中0之后的每个字符的值都应比前一个字符的值大1。

    这意味着,无论'0'的整数值是多少,'1'的整数值都是'0' + 1,'2'的整数值是'0' + 2,以此类推。使用这些信息和算术的基本规则,您可以轻松地将 char 转换为 int 并返回:

    char c = ...; // some value in the range '0' - '9'
    int int_value = c - '0';
    
    // int_value is in the range 0 - 9
    char c2 = '0' + int_value;
    

    将字母 'a' 到 'z' 转换为从 0 到 25 的数字并不容易,因为 C++ 没有指定这些字母的值是连续的。在 ASCII 中,它们是连续的,您可以编写依赖于类似于上述 '0' - '9' 的代码的代码。 (现在 ASCII 被广泛使用)。

    可移植代码将改为使用查找表或对每个字符进行特定检查:

    char int_to_char[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
    
    int char_to_int[CHAR_MAX + 1] = {};
    
    for (int i=0; i<sizeof(int_to_char); ++i) {
      char_to_int[int_to_char[i]] = i;
    }
    
    // convert a lowercase char letter to a number in the range 0 - 25:
    int i = char_to_int['d'];
    
    // convert an int in the range 0 - 25 to a char
    char c = int_to_char[25];
    

    在 C99 中,您可以直接初始化 char_to_int[] 数据而无需循环。

    int char_to_int[] = {['a'] = 0, ['b'] = 1, ['c'] = 2, ['d'] = 3, ['e'] = 4, ['f'] = 5, ['g'] = 6, ['h'] = 7, ['i'] = 8, ['j'] = 9, ['k'] = 10, ['l'] = 11, ['m'] = 12, ['n'] = 13, ['o'] = 14, ['p'] = 15, ['q'] = 16, ['r'] = 17, ['s'] = 18, ['t'] = 19, ['u'] = 20, ['v'] = 21, ['w'] = 22, ['x'] = 23, ['y'] = 24, ['z'] = 25};
    

    同样支持 C99 的 C++ 编译器也可以在 C++ 中支持这一点,作为扩展。


    这是一个完整的程序,可以生成用于这些转换的随机值。它使用 C++,加上 C99 指定的初始化扩展。

    #include <cassert>
    
    int digit_char_to_int(char c) {
      assert('0' <= c && c <= '9');
      return c - '0';
    }
    
    char int_to_digit_char(int i) {
      assert(0 <= i && i <= 9);
      return '0' + i;
    }
    
    int alpha_char_to_int(char c) {
      static constexpr int char_to_int[] = {['a'] = 0, ['b'] = 1, ['c'] = 2, ['d'] = 3, ['e'] = 4, ['f'] = 5, ['g'] = 6, ['h'] = 7, ['i'] = 8, ['j'] = 9, ['k'] = 10, ['l'] = 11, ['m'] = 12, ['n'] = 13, ['o'] = 14, ['p'] = 15, ['q'] = 16, ['r'] = 17, ['s'] = 18, ['t'] = 19, ['u'] = 20, ['v'] = 21, ['w'] = 22, ['x'] = 23, ['y'] = 24, ['z'] = 25};
    
      assert(0 <= c && c <= sizeof(char_to_int)/sizeof(*char_to_int));
      int i = char_to_int[c];
      assert(i != 0 || c == 'a');
      return i;
    }
    
    char int_to_alpha_char(int i) {
      static constexpr char int_to_char[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
    
      assert(0 <= i && i <= 25);
      return int_to_char[i];
    }
    
    #include <random>
    #include <iostream>
    
    int main() {
      std::random_device r;
      std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
      std::mt19937 m(seed);
    
      std::uniform_int_distribution<int> digits{0, 9};
      std::uniform_int_distribution<int> letters{0, 25};
    
      for (int i=0; i<20; ++i) {
        int a = digits(m);
        char b = int_to_digit_char(a);
        int c = digit_char_to_int(b);
    
        std::cout << a << " -> '" << b << "' -> " << c << '\n';
      }
    
      for (int i=0; i<20; ++i) {
        int a = letters(m);
        char b = int_to_alpha_char(a);
        int c = alpha_char_to_int(b);
    
        std::cout << a << " -> '" << b << "' -> " << c << '\n';
      }
    
    }
    

    【讨论】:

    • 请注意,虽然基本源字符集的字母不保证是连续,但它们保证是有序,即'A' &lt; 'B' &lt; ... &lt; 'Z''a' &lt; 'b' &lt; ... &lt; 'z'。 (这样做的主要好处是您不需要查找表来对字符串进行排序。)此外,EBCDIC 确实使 'A' 到 'F' 和 'a' 到 'f' 连续,所以在解析十六进制数字时依赖它是安全的。
    【解决方案2】:

    进行这种转换有两种主要方法:查找数学

    此答案中所有 ASCII 值均以十进制表示

    请注意,在 ASCII 中:'0' is 48'A' is 65'a' is 97

    查找:

    在查找版本中,您有一个 char 数组,然后将映射的值放入数组中,并创建一个整数数组以转换回来:

    为了在将char映射到int时既验证又得到对应的值:

    0 will be a sentinal value to mean not mapped: out of range    
    all results will be one more than expected
    

    unsigned char 用于确保正确处理带符号的负字符

    虽然 'C' 允许使用符号 { ['A'] = 1, ['B'] = 2,... }; , C++ 没有,因此一般可以使用以下代码来填充查找表:

    void fill_lookups(unsigned char * from_table, int from_size, int * to_table)
    {
         for (int i = 0; i < from_size; ++i)
         {
             to_table[from_table[i]]=i+1; // add one to support 0 as "out of range"
         }
    }
    
    unsigned char int_to_char[]={ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
    unsigned char int_to_lower[]={'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
                         'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
                         'u', 'v', 'w', 'x', 'y', 'z'};
    unsigned char int_to_upper[]={'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
                         'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
                         'U', 'V', 'W', 'X', 'Y', 'Z'};
    
    int char_to_int[UCHAR_MAX+2] = {};       // This will return 0 for non digits
    int letter_to_offset[UCHAR_MAX+2] = {};  // This will return 0 for non alpha
    
    fill_lookups(int_to_char, sizeof(int_to_char), char_to_int);
    fill_lookups(int_to_lower, sizeof(int_to_lower), letter_to_offset);
    fill_lookups(int_to_upper, sizeof(int_to_upper), letter_to_offset);
    
    // Helper function to check in range and always reduce in range lookups by 1
    int to_int(int * table, unsigned char c, bool * in_range)
    {
       int ret = table[c];
       if (ret)
       {
           *in_range=(1==1); // for C/C++ true
           --ret;
       }
       else
       {
           *in_range=(0==1); // for C/C++ false
       }
    
       return ret;
    }
    
    bool in_range;  // always true in these cases
    int a=to_int(char_to_int, '7', &in_range); // a is now 7
    char b=int_to_char[7]; // b is now '7'    
    int c=to_int(letter_to_offset, 'C', &in_range); // c=2
    int d=to_int(letter_to_offset, 'c', &in_range); // d=2
    char e=int_to_upper[2]; // e='C'
    char f=int_to_lower[2]; // f='c'
    

    虽然这会起作用并且如果需要验证或其他查找,这可能是有道理的,但是...

    一般来说,更好的方法是使用数学方程式

    数学上 (alpha适用于ASCII)

    假设转换已经被验证在正确的范围内: (用于 C 或 C++ 的 C 风格转换)

    注意,'0'-'9' 在 C 和 C++ 中保证是连续的

    对于 ASCII 'A-Z' 和 'a-z' 不仅是连续的,而且 'A' % 32'a' % 32 都是 1

    int a='7'-'0';         // a is now 7 in ASCII: 55-48=7
    
    char b=(char)7+'0';    // b is now '7' in ASCII: 7 + 48
    
    int c='C' % 32 - 1;    // c is now 2 in ASCII : 67 % 32 = 3 - 1 = 2
    

    -或- 我们知道它是大写的

    int c='C'-'A';         // c is now 2 in ASCII : 67 - 65 = 2
    
    
    int d='c' % 32 - 1;    // d is now 2 in ASCII : 99 % 32 = 3 - 1 = 2
    

    -或- 我们知道它是小写的

    int d='c'-'a';         // d is now 2 in ASCII : 99 - 97 = 2
    
    char e=(char)2 + 'A';  // e is 'C' in ASCII : 65 + 2 = 67
    char f=(char)2 + 'a';  // f is 'c' in ASCII : 97 + 2 = 99
    

    【讨论】:

      【解决方案3】:

      如果您知道字符c 是字母或数字,您可以这样做:

      int cton( char c )
      {
        if( 'a' <= c ) return c-'a';
        if( 'A' <= c ) return c-'A';
        return c-'0';
      }
      

      c 上添加任何需要的错误检查。

      要将整数 n 转换回 char,如果需要数字,只需使用 '0'+n,如果需要大写字母,只需使用 'A'+n,如果需要小写字母,只需使用 'a'+n

      注意:这适用于 ASCII(因为标记了 OP。但是请参阅 Pete 的信息性评论。

      【讨论】:

      • 这适用于数字,但在 C 或 C++ 中并没有要求它适用于字母。如果字符编码连续放置所有小写字母,并且连续放置所有大写字母,则它可以工作。 EBCDIC(用于 IBM 大型机)是一个经典的反例。
      • @PeteBecker 好点。但是我假设是 ASCII,因为那是 OP 的标签之一。
      【解决方案4】:

      如果我理解正确,你想这样做:

      #include <ctype.h>    /* for toupper */
      
      int digit_from_char(char c) {
          return c - '0';
      }
      
      char char_from_digit(int d) {
          return d + '0';
      }
      
      int letter_from_char(char c) {
          return toupper(c) - 'A';
      }
      
      char char_from_letter(int l) {
          return l + 'A';
      }
      

      【讨论】:

      • 虽然这个问题被 ASCII 标记这一事实是隐含的,但应该指出,这个答案确实要求字母在执行字符集中是连续的。 C 标准保证了数字而不是字母。如果使用 ASCII,则字母是连续的。
      • 我刚刚查阅了 C89 标准。你是对的,它只保证数字是连续排序的。但是正如你所说,如果执行字符集是ASCII,那么字母也是有序的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-07
      相关资源
      最近更新 更多