在C++中标记字符串

时间:2020-02-23 14:30:08  来源:igfitidea点击:

在本文中,我们将研究如何标记C++字符串。

其他语言使用string.split()有一个非常简单的解决方案。
不幸的是,本机C++不支持此方法,因此我们将向您介绍不同的方法。

方法1:转换为C字符串并使用strtok()

对于熟悉C的人来说,最明显的方法是将C++的string转换为字符数组(" C string"),然后在C字符串上使用strtok()。

由于strtok()是本机C标记器,因此这是一种可能的方式。

#include <iostream>
#include <string> //C++ Strings
#include <string.h> //For C-style strtok()

using namespace std;

void c_style_tokenizer(string inp, char* delim) {
  //Tokenizes the input string and prints the output
  //It does not return anything, since this is just for
  //illustration
  const char* c_string = inp.c_str();

  //Tokenize the C string using the delimiter
  char* token = strtok((char*)c_string, delim);

  while (token) {
      printf("Token: %s\n", token);
      //Get next token
      token = strtok(NULL, delim);
  }
}

int main() {
  //Convert the string delimiter to a char* before passing
  //it to the function, since strtok() does not support string arguments
  string input = "Hello from theitroad";
  cout << "Input String: " << input << endl;
  c_style_tokenizer(input, (char*) " ");
  return 0;
}

输出

Input String: Hello from theitroad
Token: Hello
Token: from
Token: theitroad

如您所见,的确,在使用string.c_str()转换为C字符串并使用strtok()处理它之后,我们得到了标记化的输出!

但是,此方法容易出现某些缓冲区溢出错误,因为strtok()要求输入字符串的终止符为\ 0。

由于某些原因,如果我们的输入字符串不包含它,则可能会导致错误。
另外,如果您的程序使用多个线程,则此方法可能会失败,因为strtok()使用全局变量来跟踪当前位置。

由于潜在的警告,我们将介绍一些更合适的方法。

方法2:使用正则表达式令牌迭代器(推荐)

另一种方法是使用<regex>头文件中包含的sregex_token_iterator。
这是现代C++的推荐方法,因为它使用了一些STL方法。

如果要解析空格,则首先使用正则表达式字符串" \ s +"构造一个" regex"对象,这意味着要连续捕获至少一个或者多个空格:

regex reg("\s+");

我们需要在" \ s +"中转义反斜杠,因此最终的字符串变为" \ s +"。

现在我们有了正则表达式模式,可以在执行正则表达式匹配之后使用regex_token_iterator并使用迭代器构造字符串的<vector>了。

//Courtesy: https://stackoverflow.com/a/27468529

#include <iostream>
#include <regex>
#include <string>

using namespace std;

int main()
{
  string str("Hello from theitroad");
  
  //Regex for tokenizing whitespaces
  regex reg("\s+");

  //Get an iterator after filtering through the regex
  sregex_token_iterator iter(str.begin(), str.end(), reg, -1);
  //Keep a dummy end iterator - Needed to construct a vector
  //using (start, end) iterators.
  sregex_token_iterator end;

  vector<string> vec(iter, end);

  for (auto a : vec)
  {
      cout << a << endl;
  }
}

现在,您可以将标记化的字符串作为向量。
这也非常方便,因为您可以按需获得琴弦!

输出

Hello
from
theitroad

尽管这是一种可靠的方法,但是如果您使用的是大字符串,则必须小心,因为正则表达式会影响性能,并且效率不是最高。

方法3:使用<boost>库

如果您当前的要求允许使用诸如<boost>之类的外部库,则可以使用这种方法。

我们可以在这里使用boost :: algorithm :: split函数来标记我们的输入字符串。

//Courtesy - https://stackoverflow.com/a/59552
#include <vector>
#include <boost/algorithm/string.hpp>

int main() {
  auto s = "a,b, c ,,e,f,";
  std::vector<std::string> fields;
  boost::split(fields, s, boost::is_any_of(","));
  for (const auto& field : fields)
      std::cout << "\"" << field << "\"\n";
  return 0;
}

输出

"a"
"b"
" c "
""
"e"
"f"
""

在解析逗号之后,这确实为我们提供了标记化的输出。