在C语言中将字符转换成整数

37

如果我想将单个数字char转换为其数值,例如:

char c = '5';

我想让c保存5而不是'5',这样做是否百分之百可移植?

c = c - '0';

我听说所有字符集都按顺序存储数字,所以我认为可以这样做,但我想知道是否有组织的库函数来进行此转换,以及常规的处理方式是什么。我是一个真正的初学者 :)


@Binary- 这个重复的帖子是关于 C++ 的,即使答案相同,它适用于不同的语言。 - TStamper
10个回答

28
是的,这是一个安全的转换。C语言需要它才能正常工作。ISO C标准的最新草案中保证了这一点,在第5.2.1段第2款中有说明,该草案为N1570

基本源字符集和基本执行字符集都必须包含以下成员:
[...]
十个十进制数字
0 1 2 3 4 5 6 7 8 9
[...]
在源字符集和执行字符集中,上述十进制数字列表中每个数字之后的每个字符的值都应比前一个大1。

ASCII和EBCDIC以及从它们派生的字符集都满足此要求,这就是为什么C标准能够强制实施它的原因。请注意,字母在EBCDIC中不是连续的,并且C语言不要求它们连续。
没有单独处理char的库函数,您需要先构建一个字符串:
int digit_to_int(char d)
{
 char str[2];

 str[0] = d;
 str[1] = '\0';
 return (int) strtol(str, NULL, 10);
}

你可以使用atoi()函数在获得字符串后进行转换,但是strtol()更好、更安全。
正如评论者所指出的那样,调用函数进行这种转换是极度冗余的;减去'0'的初始方法是正确的做法。我只是想展示如何使用推荐的标准方法将数字作为字符串转换为“真实”数字。

4
这是一种不必要复杂且昂贵的方法,用于执行非常直接简单的操作,即将字符型变量d转换为整型变量i(例如 int i = d - '0')。 - Binary Worrier
2
这是对你问题的回答,但不是最佳答案。这太过复杂了。你用 c=c-'0'; 回答了自己的问题 - 那才是最佳答案。 - zooropa
1
当然,使用一个函数是过度杀伤力的。也许我在那一部分应该更清楚些。 - unwind
过度设计不仅仅体现在函数方面,使用strtol也是过度设计。 - zooropa
1
这个回答没有涉及到这个问题的可移植性方面。这个函数不是可移植的。它在多字节字符集上无法工作。当尝试传递wchar_t类型的变量时,char d参数会产生编译器错误。 - zooropa

9
请尝试以下方法:
char c = '5' - '0';

6
int i = c - '0';

请注意,此方法不会对字符进行任何验证 - 例如,如果字符是 'a',那么您将得到 91-48 = 49。特别是当您处理用户或网络输入时,应该执行验证以避免程序中的错误行为。只需检查范围:

if ('0' <= c &&  c <= '9') {
    i = c - '0';
} else {
    /* handle error */
}

请注意,如果您希望进行十六进制数字的转换,您可以检查范围并执行适当的计算。
if ('0' <= c && c <= '9') {
    i = c - '0';
} else if ('a' <= c && c <= 'f') {
    i = 10 + c - 'a';
} else if ('A' <= c && c <= 'F') {
    i = 10 + c - 'A';
} else {
    /* handle error */
}

这将把一个单独的十六进制字符(大小写无关)转换为整数。


ctype.h 提供以下函数/宏来执行验证:isdigit、isxdigit、isspace 等等。请参阅 man 手册。 - James Morris

5
你可以使用标准库中的atoi函数,它可以将字符串转换为整数。

3
由于您只转换一个字符,所以函数atoi()过于复杂。 atoi()在转换数字的字符串表示时很有用。其他帖子已经给出了这方面的例子。如果我正确理解了您的帖子,您只转换一个数字字符。因此,您只会转换一个范围为0到9的字符。在仅转换一个数字字符的情况下,您建议减去'0'将会得到您想要的结果。这样做的原因是ASCII值是连续的(就像您说的那样)。因此,从数字字符中减去0的ASCII值(ASCII值为48-请参阅ASCII表获取值)将给出该数字的值。因此,对于c=c-'0'的示例,其中c='5',实际上发生的是53(数字5的ASCII值)- 48(数字0的ASCII值)= 5。
当我第一次发布这个答案时,我没有考虑到你关于在不同字符集之间完全可移植性的评论。我进一步查看了一些资料,似乎你的答案仍然基本正确。问题是你正在使用char,它是一个8位数据类型,不能适用于所有字符类型。阅读Joel Spolsky on Unicode的这篇文章,可以获得更多有关Unicode的信息。在这篇文章中,他说他使用wchar_t来表示字符。这对他很有效,并且他以29种语言发布他的网站。因此,您需要将char更改为wchar_t。除此之外,他说值为127及以下的字符基本相同。这将包括代表数字的字符。这意味着您提出的基本数学应该适用于您想要实现的内容。

1

是的。只要您使用标准 ASCII 字符,就像在此示例中一样,这是安全的。


0
您可以简单地使用atol()函数:
#include <stdio.h>
#include <stdlib.h>

int main() 
{
    const char *c = "5";
    int d = atol(c);
    printf("%d\n", d);

}

0
通常情况下,如果不能保证输入在“0”..“9”范围内,您需要执行以下检查:
if (c >= '0' && c <= '9') {
    int v = c - '0';
    // safely use v
}

另一种选择是使用查找表。您可以通过更少的代码(可能更快)进行简单的范围检查转换:

// one-time setup of an array of 256 integers;
// all slots set to -1 except for ones corresponding
// to the numeric characters
static const int CHAR_TO_NUMBER[] = {
    -1, -1, -1, ...,
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, // '0'..'9'
    -1, -1, -1, ...
};

// Now, all you need is:

int v = CHAR_TO_NUMBER[c];

if (v != -1) {
    // safely use v
}

顺便说一下,我知道这有点过头了。我只是想把它作为一个可能不会立即显而易见的替代方案呈现出来。


0

正如其他人所建议的那样,但包装在一个函数中:

int char_to_digit(char c) {
    return c - '0';
}

现在只需使用该函数。如果在以后的某个时候,您决定使用不同的方法,您只需要更改实现(性能、字符集差异等),而不需要更改调用者。

此版本假设c包含表示数字的char。您可以在调用函数之前使用ctype.h的isdigit函数进行检查。


0

由于'0','1','2'....的ASCII代码从48到57排列,它们本质上是连续的。现在,算术运算需要将char数据类型转换为int数据类型。因此,你基本上正在做的是:53-48,因此它存储值5,你可以使用任何整数操作。请注意,在从int转换回char时,编译器不会给出错误,而只是执行模256操作以将值放入其可接受的范围内。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接