题目:把字符串转换成整数
思路:
如果对于一般规则的数字“字符串”转化为数字都很容易,比如:
对于“123456”可以利用如下代码进行转化:
string str="123456";
int ans = 0;
for (int i=0; i<str.size(); ++i)
{
ans = ans * 10 + (str[i] - '0');
}
问: str[i]-'0'是什么意思?
在 C++ 中,char 类型的字符可以转换为数字类型,其中 '0' 字符对应的十进制 ASCII 码值为 48,'1' 对应的是 49,以此类推。当我们用一个 char 类型的变量或字符数组的元素存储一个数字字符时,我们可以通过减去 '0' 来将其转换为数字(即转换为该数字对应的 int 类型)。
举个例子,对于字符数组 str,str[i] 表示 str 数组中第 i 个元素所对应的字符。如果 str[i] 存储的是字符 '8',那么 str[i] - '0' 的值就是 8 。由于 '0' 字符固定是 48,所以根据 ASCII 码表,'8' 的十进制 ASCII 码值为 56,因此表达式 str[i] - '0' 就等于 56 - 48 = 8,即代表数字 8。
=>‘数字'-'0'=(int)数字
问:ans = ans * 10 + (str[i] - '0')是什么意思?
ans=0+1
ans=1*10+2=12
ans=12*10+3=123
...
int的范围为
如果超过了这两个范围该怎么办?
其实也很简单,首先判断这个数的正负,如果正数,超过了INT_MAX,就设置为INT_MAX,如果是负数,首先我们不考虑负号,如果超过了INT_MAX+1, 则就置为INT_MAX+1, 最后再根据正负号,来加负号。
//判断正负
bool neg = str[i]=='-' ? true : false;
//判断该下标对应的字符是否是数字
//如果是,就i=i
//如果否,就i=i+1,往后挪一位
i = isdigit(str[i]) ? i : i+1;
long long ans = 0L; // 因为INT_MAX+1超过了int的范围
while (i < len && isdigit(str[i])) {
ans = ans * 10 + (str[i++]-'0');
if (!neg && ans > INT_MAX) {
ans = INT_MAX;
break; //因为此处以为最大值,所以直接break
}
if (neg && ans > 1L + INT_MAX) {
ans = 1L + INT_MAX;
break;
}
}
long long ans = 0L;是什么意思?
0L 表示长整型数字 0。字符 L 是为了告诉编译器这是一个 long 类型的字面量。通常情况下,在使用 long 类型的字面量时,将其写成 0L 或者 0l(小写的 L)来区分于普通的整型字面量 0。这是因为一些编译器在处理无修饰的整数字面量时可能会发生整数溢出和类型转换等问题。所以为了确保程序可以正确地工作,我们更倾向于在使用 long 类型的字面量时加上修饰符 L。
if (!neg && ans > INT_MAX) { ans = INT_MAX; break; //因为此处以为最大值,所以直接break }
这段代码表示在 ans 大于 INT_MAX (整数类型的最大值)时,将 ans 赋值为 INT_MAX。同时,由于已经找到最大值,所以程序可以退出循环并终止后续的操作,即使用 break 语句来结束循环。在 if 语句的条件判断中,'!' 表示逻辑取反运算符,所以 !neg 的结果为 true 表示正数,并且 ans 大于 INT_MAX,进入条件成立块执行该代码。
if (neg && ans > 1L + INT_MAX) { ans = 1L + INT_MAX; break; }
这段代码进行了一个整数溢出的判断,如下所述:
- 如果要转换的整数为负且其绝对值比INT_MAX+1还大,则会发生下溢出(negative overflow),即数值过小导致运算结果不能正确表示。这是因为在int类型的范围内只能存储一定范围的整数值(符号位用来表示正负),如果超出了该范围,则会表现为异常行为,例如截断或重载。
- 为了避免下溢出的情况,我们采取了特殊处理,这里将ans设置成1L + INT_MAX,其中的1L为long类型的1,表示额外使用了一个bit位,可用于解决下溢出的问题。然后使用break语句退出循环,此时已找到最长整数子串并完成了相应的处理,不需要继续迭代后续字符。
总的来说,该段代码是为了解决边界问题而设计的,以保证函数正确性及稳定性。
最后再考虑一些特殊情况即可。
class Solution
{
public:
int StrToInt(string str)
{
const int len = str.length();
if (len == 0) return 0;
int i = 0;
while (i < len && str[i] == ' ') { ++i; } // 排除开头的空格
if (i == len) return 0;
if (!isdigit(str[i]) && str[i] != '+' && str[i] != '-') return 0;
bool neg = str[i] == '-' ? true : false;
i = isdigit(str[i]) ? i : i + 1;
long long ans = 0L;
while (i < len && isdigit(str[i]))
{
ans = ans * 10 + (str[i++] - '0');
if (!neg && ans > INT_MAX)
{
ans = INT_MAX;
break;
}
if (neg && ans > 1L + INT_MAX) {
ans = 1L + INT_MAX;
break;
}
}
if (i != len) return 0; // 不要此处,就是atoi()库函数的实现
return !neg ? static_cast<int>(ans) : static_cast<int>(-ans);
}
};
这段代码实现了将字符串转换成整数的功能,其流程如下:
- 首先判断输入字符串是否为空串,如果是,则直接返回0;
- 接着排除开头的空格符;
- 如果剩余字符中不是数字字符也不是正号或负号,则认为输入不合法,返回0;
- 确定正负性,如果是负数,则标记neg为true;
- 从第一位开始,取出连续的整数子串,并将其转换成整数。如果超过int类型能表示的范围,进行相应处理;
- 最后根据正负标志返回对应整数。
这段代码完善地考虑了各种异常情况,比如前导空格、加减号位置、多余字符等,使用了long long类型来避免数据溢出等问题。同时,代码结构清晰,变量名具有描述性,易于理解和调试。