实际中经常要用到字符串转数字,C++用std::istream的operator>>可以实现,但C语言中改怎么做呢?自己写解析函数一般应该不是什么好的选择。C运行时库提供了以下两个API可以使用:
int atoi(const char *str);
long strtol(const char *nptr, char **endptr, int base);
atoi接受的语法是[whitespace] [sign] [digits]] ,扫描参数str字符串,跳过前面的空格字符,直到遇上数字或正负符号才开始做转换,而再遇到非数字或字符串结束时才结束转换,并将结果返回。详细信息参见MSDN。
strtol接受的语法是[whitespace] [{+ | –}] [0 [{ x | X }]] [digits],将参数nptr字符串根据参数base来转换成长整型数。参数base范围从2至36,或0。参数base代表采用的进制方式,如 base值为10则采用10进制,若base值为16则采用16进制等。当base值为0时则是采用10进制做转换,但遇到如'0x'前置字符则会使用 16进制做转换。一开始strtol()会扫描参数nptr字符串,跳过前面的空格字符,直到遇上数字或正负符号才开始做转换,再遇到非数字或字符串结束时结束转换,并将结果返回。若参数endptr不为NULL,则会将遇到不合条件而终止的nptr中的字符指针由endptr返回。详细信息参见MSDN。
由此可以看出:strtol比atoi具有更大的灵活性。
下面是我自己包装的一个安全解析方法:
inline bool TryParseInt32(const char* str, int* ret, char** end = NULL)
{
char* p = NULL;
if(str == NULL || ret == NULL)
goto L_ErrorEnd;
int n = strtol(str, &p, 0);
if(n == LONG_MAX || n == LONG_MIN)
{
if(errno == ERANGE)
goto L_ErrorEnd;
}
else if(n == 0)
{
if(p == str)
goto L_ErrorEnd;
}
if(end != NULL)
*end = p;
*ret = n;
return true;
L_ErrorEnd:
if(end != NULL)
*end = const_cast<char*>(str);
if(ret != NULL)
*ret = 0;
return false;
}
inline int ParseInt32(const char* str, int defaultValue = 0)
{
int n;
if(TryParseInt32(str, &n))
return n;
else
return defaultValue;
}
用上面提供的TryParseInt32和ParseInt32方法可以安全可控的实现字符串向数字的转换。
采用上面的TryParseInt32方法,可以写出对ARGB的32bit颜色解析方法如下:
bool TryParseARGB(const std::string& str, unsigned int* result)
{
if(result == NULL)
return false;
std::string s(str);
static const std::string WHITE_SPACE(" \t\r\n\f\v");
s.erase(s.find_last_not_of(WHITE_SPACE) + 1); // trim end
s.erase(0, s.find_first_not_of(WHITE_SPACE)); // trim begin
if(s.empty())
{
if(result != NULL)
*result = 0;
return false;
}
std::string::pointer p = NULL;
int n = 0;
if(TryParseInt32(s.c_str(), &n, &p))
{
if(*p == ',')
{
std::string::pointer p1 = NULL;
int n1 = 0;
if(TryParseInt32(p + 1, &n1, &p1))
{
if(*p1 == ',')
{
std::string::pointer p2 = NULL;
int n2 = 0;
if(TryParseInt32(p1 + 1, &n2, &p2))
{
if(*p2 == ',')
{
std::string::pointer p3 = NULL;
int n3 = 0;
if(TryParseInt32(p2 + 1, &n3, &p3))
{
if(*p3 == '\0')
{
if(n >= 0 && n <= 255
&& n1 >= 0 && n1 <= 255
&& n2 >= 0 && n2 <= 255
&& n3 >= 0 && n3 <= 255)
{
*result = (n << 24) | (n1 << 16) | (n2 << 8) | n3;
return true;
}
}
}
}
else if(*p2 == '\0')
{
if(n >= 0 && n <= 255
&& n1 >= 0 && n1 <= 255
&& n2 >= 0 && n2 <= 255)
{
*result = (0xff << 24) | (n << 16) | (n1 << 8) | n2;
return true;
}
}
}
}
}
}
else if(*p == '\0')
{
*result = n;
return true;
}
}
if(result != NULL)
*result = 0;
return false;
}
inline unsigned int ParseARGB(const std::string& str, unsigned int defaultValue)
{
unsigned int ret;
if(TryParseARGB(str, &ret))
return ret;
else
return defaultValue;
}
该ARGB的颜色解析方法支持的格式为:
white := [ \t\r\n\f\v]
decnum := white*[1-9][0-9]*
octnum := white*0[0-7]*
hexnum := white*[xX][0-9a-zA-Z]+
num := decnum|octnum|hexnum
rgb := num,num,num
argb := num,num,num,num
COLOR := ^num|rgb|argb white*$
评论