令牌化
在运行时从最便宜到最昂贵的列表:
-
str::strtok
是最便宜的标准提供的标记化方法,它还允许在标记之间修改分隔符,但它在现代 C++中遇到 3 个困难:std::strtok
不能同时用于多个strings
(虽然有些实现扩展到支持这个,例如:strtok_s
)- 出于同样的原因,
std::strtok
不能同时在多个线程上使用(但是这可能是实现定义的,例如: Visual Studio 的实现是线程安全的 ) - 调用
std::strtok
会修改它正在运行的std::string
,所以它不能用于const string
s,const char*
s 或文字字符串,用std::strtok
标记任何这些,或者操作需要保留内容的std::string
,输入必须是复制,然后可以操作副本
通常,这些选项中的任何一个都会隐藏在令牌的分配成本中,但是如果需要最便宜的算法并且
std::strtok
的难度不会过度,则考虑手动解决方案 。
// String to tokenize
std::string str{ "The quick brown fox" };
// Vector to store tokens
vector<std::string> tokens;
for (auto i = strtok(&str[0], " "); i != NULL; i = strtok(NULL, " "))
tokens.push_back(i);
std::istream_iterator
迭代地使用流的提取运算符。如果输入std::string
是以空格分隔的,则可以通过消除其困难来扩展std::strtok
选项,允许内联标记化从而支持const vector<string>
的生成,并通过添加对多个分隔空白字符的支持:
// String to tokenize
const std::string str("The quick \tbrown \nfox");
std::istringstream is(str);
// Vector to store tokens
const std::vector<std::string> tokens = std::vector<std::string>(
std::istream_iterator<std::string>(is),
std::istream_iterator<std::string>());
std::regex_token_iterator
使用std::regex
进行迭代标记。它提供了更灵活的分隔符定义。例如,非分隔逗号和空格:
Version >= C++ 11
// String to tokenize
const std::string str{ "The ,qu\\,ick ,\tbrown, fox" };
const std::regex re{ "\\s*((?:[^\\\\,]|\\\\.)*?)\\s*(?:,|$)" };
// Vector to store tokens
const std::vector<std::string> tokens{
std::sregex_token_iterator(str.begin(), str.end(), re, 1),
std::sregex_token_iterator()
};
有关详细信息,请参阅 regex_token_iterator
示例 。