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

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

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

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

 

[cpp]  view plain copy
  1. //C++ code  
  2. #include   
  3. #include   
  4. #include   
  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 plain copy
  1. //C++ code  
  2. #include   
  3. #include   
  4. #include   
  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  


你可能感兴趣的:(备忘录)