令牌化

在运行时从最便宜到最昂贵的列表:

  1. str::strtok 是最便宜的标准提供的标记化方法,它还允许在标记之间修改分隔符,但它在现代 C++中遇到 3 个困难:

    • std::strtok 不能同时用于多个 strings(虽然有些实现扩展到支持这个,例如: strtok_s
    • 出于同样的原因,std::strtok 不能同时在多个线程上使用(但是这可能是实现定义的,例如: Visual Studio 的实现是线程安全的
    • 调用 std::strtok 会修改它正在运行的 std::string,所以它不能用于 const strings,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);

实例

  1. 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>());

实例

  1. 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 示例