字符串转换成整数

字符串转换成整数

题目描述:

输入一个由数字组成的字符串,把它转换成整数并输出。例如:输入字符串"123",输出整数123。

给定函数原型 int StrToInt(const char *str) ,实现字符串转换成整数的功能,不能使用库函数atoi。

分析和解法:

本题考查的实际上就是字符串转换成整数的问题,或者说是要你自行实现atoi函数。那如何实现把表示整数的字符串正确地转换成整数呢?以"123"作为例子:

  • 当我们扫描到字符串的第一个字符'1'时,由于我们知道这是第一位,所以得到数字1。
  • 当扫描到第二个数字'2'时,而之前我们知道前面有一个1,所以便在后面加上一个数字2,那前面的1相当于10,因此得到数字:1*10+2=12。
  • 继续扫描到字符'3','3'的前面已经有了12,由于前面的12相当于120,加上后面扫描到的3,最终得到的数是:12*10+3=123。

因此,此题的基本思路便是:从左至右扫描字符串,把之前得到的数字乘以10,再加上当前字符表示的数字。

这题很简单,但是依然有一些细节需要注意:

  • 空指针输入:输入的是指针,在访问空指针时程序会崩溃,因此在使用指针之前需要先判断指针是否为空。
  • 正负符号:整数不仅包含数字,还有可能是以'+'或'-'开头表示正负整数,因此如果第一个字符是'-'号,则要把得到的整数转换成负整数。
  • 非法字符:输入的字符串中可能含有不是数字的字符。因此,每当碰到这些非法的字符,程序应停止转换。
  • 整型溢出:输入的数字是以字符串的形式输入,因此输入一个很长的字符串将可能导致溢出。

上述其它问题比较好处理,但溢出问题比较麻烦,所以咱们来重点看下溢出问题。一般说来,当发生溢出时,取最大或最小的int值。即大于正整数能表示的范围时返回MAX_INT:2147483647;小于负整数能表示的范围时返回MIN_INT:-2147483648。

我们先设置一些变量:

  • sign用来处理数字的正负,当为正时sign > 0,当为负时sign < 0
  • n存放最终转换后的结果
  • c表示当前数字

而后,你可能会编写如下代码段处理溢出问题:

//当发生正溢出时,返回INT_MAX
if ((sign == '+') && (c > MAX_INT - n * 10))
{
    n = MAX_INT;
    break;
}
//发生负溢出时,返回INT_MIN
else if ((sign == '-') && (c - 1 > MAX_INT - n * 10))
{
    n = MIN_INT;
    break;
}

但当上述代码转换" 10522545459"会出错,因为正常的话理应得到MAX_INT:2147483647,但程序运行结果将会是:1932610867。

为什么呢?因为当给定字符串" 10522545459"时,而MAX_INT是2147483647,即MAX_INT(2147483647) < n*10(1052254545*10),所以当扫描到最后一个字符‘9’的时候,执行上面的这行代码:c > MAX_INT - n * 10已无意义,因为此时(MAX_INT - n * 10)已经小于0,程序已经出错。

针对这种由于输入了一个很大的数字转换之后会超过能够表示的最大的整数而导致的溢出情况,我们有两种处理方式可以选择:

  • 一个取巧的方式是把转换后返回的值n定义成long long,即long long n;
  • 另外一种则是只比较n和MAX_INT / 10的大小,即:
    • 若n > MAX_INT / 10,那么说明最后一步转换时,n*10必定大于MAX_INT,所以在得知n > MAX_INT / 10时,当即返回MAX_INT。
    • 若n == MAX_INT / 10时,那么比较最后一个数字c跟MAX_INT % 10的大小,即如果n == MAX_INT / 10且c > MAX_INT % 10,则照样返回MAX_INT。

对于上面第一种方式,虽然我们把n定义了长整型,但最后返回时系统会自动转换成整型。咱们下面主要来看第二种处理方式。

对于上面第二种方式,先举两个例子说明下:

如果我们要转换的字符串是"2147483697",那么当我扫描到字符'9'时,判断出214748369 > MAX_INT / 10 = 2147483647 / 10 = 214748364(C语言里,整数相除自动取整,不留小数),则返回MAX_INT;

如果我们要转换的字符串是"2147483648",那么判断最后一个字符'8'所代表的数字8与MAX_INT % 10 = 7的大小,前者大,依然返回MAX_INT。

一直以来,我们努力的目的归根结底是为了更好的处理溢出,但上述第二种处理方式考虑到直接计算 n * 10 + c 可能会大于MAX_INT导致溢出,那么便两边同时除以10,只比较 n和MAX_INT / 10的大小,从而巧妙的规避了计算n*10这一乘法步骤,转换成计算除法MAX_INT/10代替,不能不说此法颇妙。

如此我们可以写出正确的处理溢出的代码:

c = *str - '0';
if (sign > 0 && (n > MAX_INT / 10 || (n == MAX_INT / 10 && c > MAX_INT % 10)))
{
    n = MAX_INT;
    break;
}
else if (sign < 0 && (n > (unsigned)MIN_INT / 10 || (n == (unsigned)MIN_INT / 10 && c > (unsigned)MIN_INT % 10)))
{
    n = MIN_INT;
    break;
}

源代码如下:

#include <iostream>
#include <locale>
using namespace std;

int StrToInt(const char* str)
{
    static const int MAX_INT = (int)((unsigned)~0 >> 1);
    static const int MIN_INT = -(int)((unsigned)~0 >>1) - 1;
    unsigned int n = 0;
    //判断是否为空
    if(str == 0)
    {
        return 0;
    }
    //处理空格
    while(isspace(*str))
        ++str;
    //处理正负
    int sign = 1;
    if(*str == '+' || *str == '-')
    {
        if(*str == '-')
            sign = -1;
        ++str;  
    }
    //确定是数字后才执行循环
    while(isdigit(*str))
    {
        //处理溢出
        int c = *str - '0';
        if(sign > 0 && (n > MAX_INT / 10 || (n == MAX_INT / 10 && c > MAX_INT % 10)))
        {
            n = MAX_INT;
            break;  
        } 
        else if(sign < 0 && (n > (unsigned)MIN_INT / 10 || n == (unsigned)MIN_INT /10 && c > (unsigned)MIN_INT % 10))
        {
            n = MIN_INT;
            break;
        }
        //把之前得到的数字乘10,再加上当前字符表示的数字
        n = n * 10 + c;
        ++str; 
    } 
    return sign > 0 ? n : -n;
}

int main()
{
    char str[20];
    int num;
    cin >> str;
    num = StrToInt(str);
    cout << num << endl;
    return 0;
}

分析:这个题目很简单,但是对于一些细节的处理一定要小心,还有就是注意处理顺序。

特别注意:

  • 我们在处理数据时一定要考虑全面一些,针对于各种不同的错误给出相应的解决方案。

参考资料:《编程之法》The Art of Programming By July

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • 本系列导航:剑指offer(第二版)java实现导航帖 面试题67:把字符串转换成整数 题目要求:如题。 解题思路...
    ryderchan阅读 1,878评论 3 2
  • 把字符串转换成整数 题目描述 将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串...
    Jacinth阅读 897评论 0 0
  • 字符串转换整数需要注意下面四个问题,尤其需要注意关于溢出的处理。字符串是否为空是否包含正负号是否包含其它字符是否溢...
    鬼谷神奇阅读 296评论 0 0
  • 题目 把字符串转换成整数 注意的地方 测试用例功能测试:+1,1,0,-1边界测试:键盘输入一个大于INT_MAX...
    wuzhiguo阅读 338评论 0 0
  • 输入一个由数字组成的字符串,转换成整数 这里需要注意的是各种条件的判断,包括:输入是否合法,正负判定,溢出判定。对...
    雨_树阅读 383评论 0 0