【发布时间】:2012-02-11 21:50:23
【问题描述】:
或者反过来找第一个非数字字符。
相同的函数适用于 string 和 char* 吗?
【问题讨论】:
标签: c++ string pattern-matching
或者反过来找第一个非数字字符。
相同的函数适用于 string 和 char* 吗?
【问题讨论】:
标签: c++ string pattern-matching
从cplusplus.com你可以使用如下的isdigit函数:
// isdigit example (C++)
#include <iostream> // std::cout
#include <string> // std::string
#include <locale> // std::locale, std::isdigit
#include <sstream> // std::stringstream
int main ()
{
std::locale loc;
std::string str="1776ad";
if (isdigit(str[0],loc))
{
int year;
std::stringstream(str) >> year;
std::cout << "The year that followed " << year << " was " << (year+1) << ".\n";
}
return 0;
}
注意:isdigit 有 2 种类型,另一种版本是本地独立的和基于 ASCII 的。
【讨论】:
#include <regex>
std::string string( "I only have 3 dollars!" );
std::cout << std::regex_search( string, std::regex( "\\d+" ) ); // true
和
std::string string( "I only have three dollars!" );
std::cout << std::regex_search( string, std::regex( "\\d+" ) ); // false
【讨论】:
已经有几个人提到使用isdigit()。但是请注意,这并非完全无关紧要,因为可以对char 进行签名,这会导致将负值传递给isdigit()。但是,此函数只能取正值。也就是说,你想要类似的东西:
if (s.end() == std::find_if(s.begin(), s.end(),
[](unsigned char c)->bool { return !isdigit(c); })) {
std::cout << "string '" << s << "' contains only digits\n";
}
似乎转换为unsigned char 的原因并不明显。因此,以下是各自标准中的相关引述:
根据 ISO/IEC 9899:2011(或 ISO/IEC 9899:1999)7.4 第 1 段,以下适用于来自 <ctype.h> 的函数的参数:
... 在所有情况下,参数都是
int,其值应为 可表示为unsigned char或应等于宏EOF的值。如果 参数有任何其他值,行为未定义。
不幸的是,C++ 标准没有指定char 是无符号类型。相反,它在 ISO/IEC 14882:2011 3.9.1 [basic.fundamental] 第 1 段中指定:
...
char对象是否可以保存负值是实现定义的。 ...
显然,负值不能表示为unsigned char。也就是说,如果char 在实现上使用签名类型(实际上有几个这样做,例如,它是在 MacOS 上使用 gcc 或 clang 签名的),那么调用任何<ctype.h> 函数都会导致危险未定义的行为。
现在,为什么转换为 unsigned char 会做正确的事情?
根据 4.7 [conv.integral] 第 2 段:
如果目标类型是无符号的,则结果值是与源整数一致的最小无符号整数(模 2n,其中 n 是用于表示无符号类型的位数)。 [ 注意:在二进制补码表示中,这种转换是概念性的,位模式没有变化(如果没有截断)。 ——尾注]
也就是说,从 [可能] 签名的 char 到 unsigned char 的转换是明确定义的,并导致结果在 <ctype.h> 函数的允许范围内。
【讨论】:
char 转换为unsigned char,可能会产生溢出或UB,比依靠isdigit() 检查@987654342 更好。 @ 属于['0'..'9'],具体以int 作为输入?如果可能的话,任何引用或理由都可能会有所帮助。
char 到unsigned char 的转换不会溢出或任何东西。即使char 已签名并且字符在负数范围内,它将保留原始位,但会在isdigit() 的定义范围内产生一个值。相关引用在 C 的 7.4 第 1 段中。
与 Misha 的回答相同,但更正确:sscanf(buf, "%*u%*c")==1。
如果%d 数字提取失败,scanf 返回 0,如果 %c 捕获的数字后面还有任何内容,则返回 2。而且由于* 阻止了值的存储,因此您甚至不会出现溢出。
【讨论】:
当然,有很多方法可以只测试字符串中的数字字符。两种可能的方法是:
bool is_digits(const std::string &str)
{
return str.find_first_not_of("0123456789") == std::string::npos;
}
或
bool is_digits(const std::string &str)
{
return std::all_of(str.begin(), str.end(), ::isdigit); // C++11
}
【讨论】:
std::isdigit 也不起作用。
isdigit 函数(来自<cctype> 和<locale> 标头)。 See this related answer.
std::string 参数(复制可能会很昂贵)。它们也是const,以确保我不会修改调用者的变量。以这种方式使用 const 引用在 C++ 编程中非常常见。
如果严格要求您可以准确找到第一个非字符数字的位置,那么您必须检查每个字符。如果没有,我会使用这样的东西:
unsigned safe_atoi(const std::string& a)
{
std::stringstream s(a);
unsigned b;
s >> b;
return b;
}
【讨论】:
cctype 头文件有很多字符分类函数,您可以对字符串中的每个字符使用这些函数。对于数字检查,应为 isdigit。
下面的程序展示了如何检查 C 或 C++ 字符串的每个字符(检查实际字符的过程几乎相同,唯一真正的区别是如何获取长度):
#include <iostream>
#include <cstring>
#include <cctype>
int main (void) {
const char *xyzzy = "42x";
std::cout << xyzzy << '\n';
for (int i = 0; i < std::strlen (xyzzy); i++) {
if (! std::isdigit (xyzzy[i])) {
std::cout << xyzzy[i] << " is not numeric.\n";
}
}
std::string plugh ("3141y59");
std::cout << plugh << '\n';
for (int i = 0; i < plugh.length(); i++) {
if (! std::isdigit (plugh[i])) {
std::cout << plugh[i] << " is not numeric.\n";
}
}
return 0;
}
【讨论】:
isdigit(int) 告诉您字符是否为数字。如果你打算假设 ASCII 和 base 10,你也可以使用:
int first_non_digit_offset= strspn(string, "0123456789")
【讨论】: