关于atoi字符串转换成int整数的一些问题

来源:互联网 发布:sd卡检测软件 编辑:程序博客网 时间:2024/05/12 06:47

http://blog.csdn.net/silvergingko/article/details/6070138

在编程中,经常需要将一个字符串中包含的数字转换成整数进行处理。标准C函数atoi及Unicode版本的wtoi都可以用来实现该任务。然而,在很多情况下,我并不喜欢使用这个函数,因为它无法满足我对转换结果的要求。

以下,先给出一段对atoi进行测试的代码。测试结果使用注释的形式给出:

 

[cpp] view plaincopy
  1. //C++ code  
  2. #include <Windows.h>  
  3. #include <errno.h>  
  4. #include <tchar.h>  
  5.   
  6. int _tmain(int argc, _TCHAR* argv[])  
  7. {  
  8.   
  9. //-----------------------------------------------------------------------------  
  10. // 测试用例。  
  11. //-----------------------------------------------------------------------------  
  12.   
  13.     _TCHAR* s1 = _T("345");  
  14.     _TCHAR* s2 = _T("-345");  
  15.     _TCHAR* s3 = _T("0");  
  16.     _TCHAR* s4 = _T("2147483647");//INT_MAX。  
  17.     _TCHAR* s5 = _T("2147483648");//INT_MAX+1。  
  18.     _TCHAR* s6 = _T("-2147483648");//INT_MIN。  
  19.     _TCHAR* s7 = _T("-2147483649");//INT_MIN-1。  
  20.     _TCHAR* s8 = _T("9876543210");  
  21.     _TCHAR* s9 = _T("");  
  22.     _TCHAR* s10 = _T("q");  
  23.     _TCHAR* s11 = _T("345q");  
  24.     _TCHAR* s12 = NULL;  
  25.     _TCHAR ch = _T('/0');  
  26.   
  27. //-----------------------------------------------------------------------------  
  28. // _ttoi function test.  
  29. //-----------------------------------------------------------------------------  
  30.   
  31.     int num = 0;  
  32.     errno_t err = 0;  
  33.   
  34.     _set_errno(0);  
  35.     num = _ttoi(s1);//num = 345。  
  36.     _get_errno(&err);//err = 0。  
  37.   
  38.     _set_errno(0);  
  39.     num = _ttoi(s2);//num = -345。  
  40.     _get_errno(&err);//err = 0。  
  41.   
  42.     _set_errno(0);  
  43.     num = _ttoi(s3);//num = 0。  
  44.     _get_errno(&err);//err = 0。  
  45.   
  46.     _set_errno(0);  
  47.     num = _ttoi(s4);//num = 2147483647。  
  48.     _get_errno(&err);//err = 0。  
  49.   
  50.     _set_errno(0);  
  51.     num = _ttoi(s5);//num = 2147483647。  
  52.     _get_errno(&err);//err = 34。  
  53.   
  54.     _set_errno(0);  
  55.     num = _ttoi(s6);//num = -2147483648。  
  56.     _get_errno(&err);//err = 0。  
  57.   
  58.     _set_errno(0);  
  59.     num = _ttoi(s7);//num = -2147483648。  
  60.     _get_errno(&err);//err = 34。  
  61.   
  62.     _set_errno(0);  
  63.     num = _ttoi(s8);//num = 2147483647。  
  64.     _get_errno(&err);//err = 34。  
  65.   
  66.     _set_errno(0);  
  67.     num = _ttoi(s9);//num = 0。  
  68.     _get_errno(&err);//err = 0。  
  69.   
  70.     _set_errno(0);  
  71.     num = _ttoi(s10);//num = 0。  
  72.     _get_errno(&err);//err = 0。  
  73.   
  74.     _set_errno(0);  
  75.     num = _ttoi(s11);//num = 345。  
  76.     _get_errno(&err);//err = 0。  
  77.   
  78.     //_set_errno(0);  
  79.     //num = _ttoi(s12);//抛出异常。  
  80.     //_get_errno(&err);  
  81.   
  82.     _set_errno(0);  
  83.     num = _ttoi(&ch);//num = 0。  
  84.     _get_errno(&err);//err = 0。  
  85.   
  86. //-----------------------------------------------------------------------------  
  87. // End of _ttoi function test.  
  88. //-----------------------------------------------------------------------------  

 

从以上测试的结果中,我比较关心的是第41行和第69行代码的结果,当字符串为s3,函数处理返回结果为0,这是理所当然的,

可是,字符串s10返回的结果也是0,且atoi函数并不会为遇上非数字字符而调用_set_errno,对于这样的行为我感到非常诧异,因为当s3和s10放在一起时,显然这两个字符串是完全不同的,作为atoi的调用者,将无法判断传给函数的是s3还是s10。如果程序需要针对用户给出的一个非数字字符串,提示用户字符串内容不正确,首先需要自己扫描字符串内容来判断字符串内容是否符号要求,然后才能调用atoi函数。这样对整个字符串的转换工作至少需要扫描字符串两次才能完成。并且,根据MSDN介绍,StrToIntXXX系列函数的工作方式和atoi类似。基于以上的情况,只能自己动手写一个字符串转换函数。

以下给出自定义函数Str2iNum和测试用例的完整代码:

[cpp] view plaincopy
  1. //C++ code  
  2. #include <Windows.h>  
  3. #include <errno.h>  
  4. #include <tchar.h>  
  5.   
  6. //函数前导声明。  
  7. BOOL __stdcall Str2iNumW(LPCWSTRint *, BOOL);  
  8. BOOL __stdcall Str2iNumA(LPCSTRint *, BOOL);  
  9.   
  10. //宏定义。  
  11. #undef Str2iNum  
  12. #ifdef _UNICODE  
  13. #define Str2iNum Str2iNumW  
  14. #else  
  15. #define Str2iNum Str2iNumA  
  16. #endif  
  17.   
  18. int _tmain(int argc, _TCHAR* argv[])  
  19. {  
  20.   
  21. //-----------------------------------------------------------------------------  
  22. // 测试用例。  
  23. //-----------------------------------------------------------------------------  
  24.   
  25.     _TCHAR* s1 = _T("345");  
  26.     _TCHAR* s2 = _T("-345");  
  27.     _TCHAR* s3 = _T("0");  
  28.     _TCHAR* s4 = _T("2147483647");//INT_MAX。  
  29.     _TCHAR* s5 = _T("2147483648");//INT_MAX+1。  
  30.     _TCHAR* s6 = _T("-2147483648");//INT_MIN。  
  31.     _TCHAR* s7 = _T("-2147483649");//INT_MIN-1。  
  32.     _TCHAR* s8 = _T("9876543210");  
  33.     _TCHAR* s9 = _T("");  
  34.     _TCHAR* s10 = _T("q");  
  35.     _TCHAR* s11 = _T("345q");  
  36.     _TCHAR* s12 = NULL;  
  37.     _TCHAR ch = _T('/0');  
  38.   
  39. //-----------------------------------------------------------------------------  
  40. // Str2iNum function test.  
  41. //-----------------------------------------------------------------------------  
  42.   
  43.     BOOL bRet = FALSE;  
  44.     DWORD dwErr = ERROR_SUCCESS;  
  45.   
  46. //在 WinError.h 定义的常量宏。  
  47. //#define ERROR_BAD_FORMAT          11L  
  48. //#define ERROR_INVALID_DATA        13L  
  49. //#define ERROR_INVALID_PARAMETER   87L  
  50.   
  51.   
  52.     bRet = Str2iNum(s1, &num, FALSE);//num = 345, bRet = TRUE。  
  53.   
  54.     bRet = Str2iNum(s2, &num, FALSE);//num = -345, bRet = TRUE。  
  55.   
  56.     bRet = Str2iNum(s3, &num, FALSE);//num = 0, bRet = TRUE。  
  57.   
  58.     bRet = Str2iNum(s4, &num, FALSE);//num = 2147483647, bRet = TRUE。  
  59.   
  60.     SetLastError(NO_ERROR);  
  61.     bRet = Str2iNum(s5, &num, FALSE);//num = 2147483647, bRet = FALSE。  
  62.     dwErr = GetLastError();//dwErr = 13。  
  63.   
  64.     bRet = Str2iNum(s6, &num, FALSE);//num = -2147483648, bRet = TRUE。  
  65.   
  66.     SetLastError(NO_ERROR);  
  67.     bRet = Str2iNum(s7, &num, FALSE);//num = -2147483648, bRet = FALSE。  
  68.     dwErr = GetLastError();//dwErr = 13。  
  69.   
  70.     SetLastError(NO_ERROR);  
  71.     bRet = Str2iNum(s8, &num, FALSE);//num = -2147483648, bRet = FALSE。  
  72.     dwErr = GetLastError();//dwErr = 13。  
  73.   
  74.     SetLastError(NO_ERROR);  
  75.     bRet = Str2iNum(s9, &num, FALSE);//num = -2147483648, bRet = FALSE。  
  76.     dwErr = GetLastError();//dwErr = 11。  
  77.   
  78.     SetLastError(NO_ERROR);  
  79.     bRet = Str2iNum(s10, &num, FALSE);//num = -2147483648, bRet = FALSE。  
  80.     dwErr = GetLastError();//dwErr = 11。  
  81.   
  82.     SetLastError(NO_ERROR);  
  83.     bRet = Str2iNum(s11, &num, FALSE);//num = -2147483648, bRet = FALSE。  
  84.     dwErr = GetLastError();//dwErr = 11。  
  85.   
  86.     SetLastError(NO_ERROR);  
  87.     bRet = Str2iNum(s12, &num, FALSE);//num = -2147483648, bRet = FALSE。  
  88.     dwErr = GetLastError();//dwErr = 87。  
  89.   
  90.     SetLastError(NO_ERROR);  
  91.     bRet = Str2iNum(&ch, &num, FALSE);//num = -2147483648, bRet = FALSE。  
  92.     dwErr = GetLastError();//dwErr = 11。  
  93.   
  94.     bRet = Str2iNum(_T("    /t  123"), &num, TRUE);//num = 123, bRet = TRUE。  
  95.   
  96. //-----------------------------------------------------------------------------  
  97. // End of Str2iNum function test.  
  98. //-----------------------------------------------------------------------------  
  99.   
  100.     return 0;  
  101. }  
  102.   
  103. #pragma region Str2iNum2W  
  104. // 函数功能: 将字符串转换成对应的int型整数(Unicode版本)。  
  105. // 参数:  
  106. // pszNum: 源字符串。  
  107. // pnNum: 转换后的int型整数的存储指针。  
  108. // bSpaceLead: 前导空格。  
  109. // 返回值: 成功返回TRUE,失败返回FALSE。  
  110.   
  111. BOOL __stdcall Str2iNumW(LPCWSTR pszNum, int *pnNum, BOOL bSpaceLead)  
  112. {  
  113.     __int64 num = 0;  
  114.     int sign = 1;  
  115.     size_t len = 0;  
  116.     BOOL bRet = FALSE;  
  117.     WCHAR ch = 0;  
  118.   
  119.     if (pszNum == NULL || pnNum == NULL)  
  120.     {  
  121.         SetLastError(ERROR_INVALID_PARAMETER);  
  122.         return bRet;  
  123.     }  
  124.   
  125.     //跳过前导空格。  
  126.     if (bSpaceLead == TRUE)  
  127.     {  
  128.         while (*pszNum == _T(' ') || *pszNum == _T('/t'))  
  129.             pszNum++;  
  130.     }  
  131.   
  132.     //跳过符号位。  
  133.     ch = *pszNum;  
  134.     if (ch == _T('-'))  
  135.     {  
  136.         sign = -1;  
  137.         pszNum++;  
  138.     } else if (ch == _T('+'))  
  139.     {  
  140.         pszNum++;  
  141.     }  
  142.   
  143.     //非数字字符。  
  144.     if (*pszNum < _T('0') || *pszNum > _T('9'))  
  145.     {  
  146.         SetLastError(ERROR_BAD_FORMAT);  
  147.         return bRet;  
  148.     }  
  149.   
  150.     //第一个字符是数字字符"0"。  
  151.     if (*pszNum == _T('/0'))  
  152.     {  
  153.         if (pszNum[1] == _T('/0'))  
  154.         {  
  155.             *pnNum = 0;  
  156.             return TRUE;  
  157.         }  
  158.         else  
  159.         {  
  160.             SetLastError(ERROR_BAD_FORMAT);  
  161.             return bRet;  
  162.         }  
  163.     }  
  164.   
  165.     while (TRUE)  
  166.     {  
  167.         ch = *pszNum++;  
  168.   
  169.         //数字字符。  
  170.         if (ch >= _T('0') && ch <= _T('9'))  
  171.         {  
  172.             len++;  
  173.       
  174.             //int型整数最多十位。  
  175.             if (len > 10)  
  176.             {  
  177.                 SetLastError(ERROR_INVALID_DATA);  
  178.                 break;  
  179.             }  
  180.   
  181.             num = num * 10 + (ch - _T('0'));  
  182.   
  183.             continue;  
  184.         }  
  185.         //字符串终止符。  
  186.         else if (ch == _T('/0'))  
  187.         {  
  188.             num = sign * num;  
  189.             if (num <= INT_MAX && num >= INT_MIN)  
  190.             {  
  191.                 *pnNum = (int)num;  
  192.                 bRet = TRUE;  
  193.                 break;  
  194.             }  
  195.             //溢出。  
  196.             else  
  197.             {  
  198.                 SetLastError(ERROR_INVALID_DATA);  
  199.                 break;  
  200.             }  
  201.         }  
  202.         //非数字字符。  
  203.         else  
  204.         {  
  205.             SetLastError(ERROR_BAD_FORMAT);  
  206.             break;  
  207.         }  
  208.     }  
  209.   
  210.     return bRet;  
  211. }  
  212. #pragma endregion  
  213.   
  214. #pragma region Str2iNumA  
  215. // 参数功能:Ansi版本的Str2iNumW。  
  216.   
  217. BOOL __stdcall Str2iNumA(LPCSTR pszNum, int *pnNum, BOOL bSpaceLead)  
  218. {  
  219.     //int类型的数字最多是10位,大于10的最小2次幂是16。  
  220.     WCHAR szNum[16] = { 0 };  
  221.     int nRet = 0;  
  222.     size_t cbLen = 0;  
  223.   
  224.     if (pszNum == NULL || pnNum == NULL)  
  225.     {  
  226.         SetLastError(ERROR_INVALID_PARAMETER);  
  227.         return FALSE;  
  228.     }  
  229.   
  230.     //跳过前导空格。  
  231.     if (bSpaceLead == TRUE)  
  232.     {  
  233.         while (*pszNum == _T(' ') || *pszNum == _T('/t'))  
  234.             pszNum++;  
  235.     }  
  236.   
  237.     cbLen = strnlen_s(pszNum, 16);  
  238.   
  239.     //过滤较长的恶意字符串。  
  240.     if (cbLen == 16)  
  241.     {  
  242.         SetLastError(ERROR_BAD_FORMAT);  
  243.         return FALSE;  
  244.     }  
  245.   
  246.     nRet = MultiByteToWideChar(CP_ACP, 0, pszNum, -1, szNum, 16);  
  247.     if (nRet == 0)  
  248.     {  
  249.         return FALSE;  
  250.     }  
  251.   
  252.     return Str2iNumW(szNum, pnNum, bSpaceLead);  
  253. }  
  254. #pragma endregion  


原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 宝宝流鼻涕鼻子擦红了怎么办 上班忘记穿内衣了怎么办 早上上班忘记穿内衣了怎么办 高中知识都忘了怎么办 留鼻涕跟水一样怎么办 鼻涕像水一样流怎么办 肩膀很疼怎么办睡不着觉 夏天穿内衣出汗后很臭怎么办 脸过敏后严重缺水怎么办 过敏后脸上反复出现湿疹怎么办 孕妇脸上长湿疹过敏红肿怎么办 一岁宝宝一直流鼻涕怎么办 三岁宝宝一直流鼻涕怎么办 3岁宝宝一直流鼻涕不好怎么办 7岁半边鼻子不通气怎么办 感冒流鼻涕鼻子不通气怎么办 鼻子不通气干的怎么办 7个月婴儿流鼻涕怎么办 鼻子火辣辣的光想流鼻涕怎么办? 三岁宝宝鼻塞流鼻涕怎么办 一岁宝宝感冒流鼻涕鼻塞怎么办 1岁宝宝鼻塞流鼻涕怎么办 一岁多宝宝感冒鼻塞流鼻涕怎么办 宝宝鼻塞流鼻涕怎么办速效办法 八个月宝宝感冒流鼻涕鼻塞怎么办 7岁儿童流清鼻涕怎么办 1岁的宝宝流鼻涕怎么办 3岁宝宝鼻塞严重怎么办 宝宝流鼻涕2个月怎么办 一个月宝宝流鼻子怎么办 六个月宝宝流清水鼻涕怎么办 婴儿流鼻涕怎么办最简单方法 婴儿咳嗽流鼻涕怎么办最简单方法 6个月小孩流鼻涕怎么办 宝宝风寒感冒咳嗽流鼻涕怎么办 三个月宝宝流清鼻涕怎么办 三个月宝宝留清鼻涕怎么办 三个月婴儿流清鼻涕怎么办 宝宝7个月流鼻涕怎么办 6个月孩子流鼻涕怎么办 十一个月孩子感冒流鼻涕怎么办