如何将单个字符转换为整数

72

我有一个数字字符串, 例如"123456789", 我需要提取其中的每个数字用于计算。我当然可以通过索引访问每个字符,但如何将其转换为整数呢?

我查看了atoi(),但它以字符串作为参数。因此,我必须将每个字符转换为字符串,然后调用atoi()函数。是否有更好的方法?


为什么你需要逐个提取,而不是将一系列数字视为实际数字? - Calyth
该字符串实际上不是一个数字,而是由单个数字组成的,确切地说,是社会安全号码。我想运行一个计算来验证这个社会安全号码。 - jonsb
3
请问您需要翻译的是这个问题的答案还是这个问题本身? - user202729
11个回答

173
你可以利用一个事实,即数字的字符编码按顺序排列,从48(表示'0')到57(表示'9')。这对于ASCII、UTF-x和几乎所有其他编码都成立(更多信息请参见下面的评论)。
因此,任何数字的整数值都是该数字减去'0'(或48)。
char c = '1';
int i = c - '0'; // i is now equal to 1, not '1'

与...同义

char c = '1';
int i = c - 48; // i is now equal to 1, not '1'

然而,我发现第一个c - '0'更易读。


3
有没有一种编码方式使得 '9'-'0'!= 9?我甚至不确定这样的编码是否符合ISO C ++标准。 - MSalters
5
关于编码和数字顺序,我在这个问题 http://stackoverflow.com/questions/782373/are-digits-represented-in-sequence-in-all-text-encodings 上提出了这个问题。简短的回答是“基于Ascii或EBCDIC的任何编码都是有序的”(这意味着我们在日常生活和网络中遇到的99.9%的编码都是如此)。有趣的是,C/C++标准似乎声明它们仅支持数字顺序有序的编码。 - Binary Worrier
有没有一种编码方式,其中不包括 '0' < '1' < '2' < '3' 的规则?这将是一个非常奇怪的决定。 - Friedrich
6
C++标准保证字符集中的'0'到'9'相邻且按正确顺序排列。因此,表达式c - '0'在所有系统上都可用,而例如在EBCDIC系统上表达式c - 48将无法使用。 - M.M
@Friedrich——在数字化之前的电信领域中,使用脉冲拨号技术时,“0”数字代表10个脉冲。拨打数字“9”需要多少个脉冲?拨打数字“0”呢?为什么键盘上的“0”在“9”的后面(数字键盘上则是在“9”的下方)? - Jonathan Leffler
2
请注意,C11 §5.2.1 字符集 ¶3 规定:在源和执行的基本字符集中,上述十进制数字列表中每个字符的值都应比前一个大一。 C++标准也将有类似的规则。 - Jonathan Leffler

24
#define toDigit(c) (c-'0')

8

或者您可以使用“正确”的方法,类似于您原来的 atoi 方法,但是改用 std::stringstream。这种方法可以处理字符和字符串作为输入。(boost::lexical_cast 是另一种更方便的语法选项)

(atoi 是一个古老的 C 函数,通常建议尽可能使用更灵活和类型安全的 C++ 替代方法。std::stringstream 可以覆盖从字符串到数值类型的转换)


不错,我的 C++ 已经生疏了,我还以为有更安全的方法,但是没有时间去查看。 - Binary Worrier

4

您可以利用 atoi() 函数

#include <stdlib.h>
#include <stdio.h>
int main(int argc, char* argv[]){
    int num ;
    num = atoi(argv[1]);
    printf("\n%d", num);
}

1
atoi 将一串字符转换为数字,而不是像 OP 想要的那样将单个字符转换。 - osvein

3
提供的答案非常好,只要您想处理阿拉伯数字,并且在编码中工作,这些数字是顺序的,并且处于与 ASCII 相同的位置。
这几乎总是情况。
如果不是这样,那么你需要一个合适的库来帮助你。
让我们从ICU开始。
  1. 首先将字节串转换为 Unicode 字符串。(留给读者练习)。
  2. 然后使用uchar.h查看每个字符。
  3. 如果字符是 UBool u_isdigit (UChar32 c)
  4. 那么值就是 int32_t u_charDigitValue ( UChar32 c )
或者也许 ICU 有一些函数可以为您执行此操作-我还没有详细研究它。

2
#include<iostream>
#include<stdlib>
using namespace std;

void main()
{
     char ch;
     int x;
     cin >> ch;
     x = char (ar[1]);
     cout << x;
}

1

如果您担心编码问题,您可以始终使用switch语句。

只需小心保持这些大数字的格式。在某些系统中,整数的最大大小仅为65,535(32,767有符号)。在其他系统中,您有2,147,483,647(或4,294,967,295无符号)。


一个简单的switch语句如何帮助处理不同的编码? - gimpf
因为Unicode数字恰好覆盖了10个位置,而ASCII数字也恰好覆盖了10个位置。我有什么遗漏吗? - user54650
2
你不需要担心编码问题。即使数字不是从48到58,这些数字在编码中仍然是相邻的。因此,你仍然可以从任何数字中减去'0'并得到正确的结果。我相信我在标准中读到过这一点。 - Johannes Schaub - litb

1

使用以下方式有什么问题吗?

int CharToInt(const char c)
{
    switch (c)
    {
    case '0':
        return 0;
    case '1':
        return 1;
    case '2':
        return 2;
    case '3':
        return 3;
    case '4':
        return 4;
    case '5':
        return 5;
    case '6':
        return 6;
    case '7':
        return 7;
    case '8':
        return 8;
    case '9':
        return 9;
    default:
        return 0;
    }
}

11
除了这是一大堆重复的代码用来做相当琐碎的事情之外,没有什么不同。Binary Worrier的答案要简单得多,只要知道输入编码,并允许使用这个快捷方式。我的版本也更短,而且无论编码如何都可以工作,但速度会慢一些。 - jalf
@jalf的二进制战士的答案保证适用于任何符合C++或POSIX编码规范的编码方式。 - osvein

1
通过这种方式,您可以轻松地将字符转换为整数或将整数转换为字符:
int charToInt(char c)
{
   int arr[]={0,1,2,3,4,5,6,7,8,9};
   return arr[c-'0'];
}

对于所有有效的值xarr[x] == x。由于访问数组越界是未定义行为,因此可以假设arr[x] == x 对于所有的x成立。 - user202729

1

我同意@jalf的观点。使用sstream库和stoi函数似乎是推荐的方法:

#include <iostream>
#include <string>
#include <sstream>

using namespace std;

int main() {

  stringstream st;
  st << 1  << '2';

  cout << stoi(st.str()) + 1; 

  return 0;
}

Output

13

我是一名新手C++学生,但长期从事LAMP堆栈开发。我希望字符串类有更多的东西可以平稳地在字符和字符串之间过渡,但我还没有找到本地支持的这种东西。

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