【问题标题】:String Tokenizer with multiple delimiters including delimiter without Boost具有多个分隔符的字符串标记器,包括没有 Boost 的分隔符
【发布时间】:2015-09-18 02:35:18
【问题描述】:

我需要在 C++ 中创建字符串解析器。我尝试使用

vector<string> Tokenize(const string& strInput, const string& strDelims)
{
 vector<string> vS;

 string strOne = strInput;
 string delimiters = strDelims;

 int startpos = 0;
 int pos = strOne.find_first_of(delimiters, startpos);

 while (string::npos != pos || string::npos != startpos)
 {
  if(strOne.substr(startpos, pos - startpos) != "")
   vS.push_back(strOne.substr(startpos, pos - startpos));

  // if delimiter is a new line (\n) then add new line
  if(strOne.substr(pos, 1) == "\n")
   vS.push_back("\\n");
  // else if the delimiter is not a space
  else if (strOne.substr(pos, 1) != " ")
   vS.push_back(strOne.substr(pos, 1));

  if( string::npos == strOne.find_first_not_of(delimiters, pos) )
   startpos = strOne.find_first_not_of(delimiters, pos);
  else
   startpos = pos + 1;

        pos = strOne.find_first_of(delimiters, startpos);

 }

 return vS;
}

这适用于 2X+7cos(3Y)

(tokenizer("2X+7cos(3Y)","+-/^() \t");)

但给出 2X 的运行时错误

我需要非 Boost 解决方案。

我尝试使用C++ String Toolkit (StrTk) Tokenizer

std::vector<std::string> results;
strtk::split(delimiter, source,
             strtk::range_to_type_back_inserter(results),
             strtk::tokenize_options::include_all_delimiters);

 return results; 

但它不会将令牌作为单独的字符串。

例如:如果我将输入设为 2X+3Y

输出向量包含

2X+

3 年

【问题讨论】:

  • 大概你需要保护pos = str.find_first_of(delimiters, lastPos)免受lastPosnpos的情况。
  • 如果您要使用非标准库(表面上是this)显示代码,您应该在问题中命名,提供链接,并考虑在您的问题中添加相关标签。
  • 我添加了 strtk 是因为该解决方案无法解决我的问题。现在将添加链接

标签: c++ string tokenize


【解决方案1】:

可能发生的情况是当通过npos 时发生崩溃:

lastPos = str.find_first_not_of(delimiters, pos);

只需在循环中添加中断,而不是依赖 while 子句来中断它。

if (pos == string::npos)
  break;
lastPos = str.find_first_not_of(delimiters, pos);

if (lastPos == string::npos)
  break;
pos = str.find_first_of(delimiters, lastPos);

【讨论】:

    【解决方案2】:

    循环退出条件被破坏:

    while (string::npos != pos || string::npos != startpos)
    

    允许输入,例如 pos = npos 和 startpos = 1。

    所以

    strOne.substr(startpos, pos - startpos)
    strOne.substr(1, npos - 1)
    

    end 不是 npos,所以 substr 不会停在它应该停在的地方并且 BOOM!

    如果 pos = npos 且 startpos = 0,

    strOne.substr(startpos, pos - startpos)
    

    活着,但是

    strOne.substr(pos, 1) == "\n"
    strOne.substr(npos, 1) == "\n"
    

    死了。也一样

    strOne.substr(pos, 1) != " "
    

    很遗憾,我没时间了,现在无法解决这个问题,但 QuestionC 的想法是正确的。更好的过滤。大致如下:

        if (string::npos != pos)
        {
            if (strOne.substr(pos, 1) == "\n") // can possibly simplify this with strOne[pos] == '\n'
                vS.push_back("\\n");
            // else if the delimiter is not a space
            else if (strOne[pos] != ' ')
                vS.push_back(strOne.substr(pos, 1));
        }
    

    【讨论】:

      【解决方案3】:

      如果您能分享一些有关您的环境的信息,那就太好了。你的程序在我的 Fedora 20 上使用 g++ 运行良好,输入值为 2X。

      【讨论】:

      • 这个答案更适合作为评论,而不是问题的真正答案
      • 我在 Win 8.1 中使用 MinGW C++ 编译器
      【解决方案4】:

      我创建了一个小函数,将字符串拆分为子字符串(存储在向量中),它允许您设置要将哪些字符视为空格。普通空白仍将被视为空白,因此您不必定义它。实际上,它所做的只是将您定义为空白的字符转换为实际的空白(空格字符'')。然后它在一个流(stringstream)中运行它以分离子字符串并将它们存储在一个向量中。对于这个特定问题,这可能不是您所需要的,但也许它可以给您一些想法。

      // split a string into its whitespace-separated substrings and store
      // each substring in a vector<string>. Whitespace can be defined in argument
      // w as a string (e.g. ".;,?-'")
      vector<string> split(const string& s, const string& w)
      {
          string temp{ s };
          // go through each char in temp (or s)
          for (char& ch : temp) {     
              // check if any characters in temp (s) are whitespace defined in w
              for (char white : w) {  
                  if (ch == white)
                      ch = ' ';       // if so, replace them with a space char (' ')
              }
          }
      
          vector<string> substrings;
          stringstream ss{ temp };
      
          for (string buffer; ss >> buffer;) {
              substrings.push_back(buffer);
          }
          return substrings;
      }
      

      【讨论】:

      • 很有趣,但对蛮力很重。您是否考虑过在 w 中使用 set 代替 string?您可以将 for (char white : w) 循环减少到 if (w.find(ch) != w.end()) 不是很棒但不是 N 平方。
      • 嗯...我没想到。老实说,我对 C++ 和一般编程还很陌生,所以有很多我不知道的。不过,我必须尝试一下并测试两种方式的性能。我同意我现在这样做的方式偏重。嘿,它虽然有效。我总是想尝试一种不同的、更有效的方法。感谢您的评论。
      猜你喜欢
      • 1970-01-01
      • 2019-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-29
      • 2021-11-10
      • 1970-01-01
      相关资源
      最近更新 更多