Строковый токенизатор с несколькими разделителями, включая разделитель без Boost

Мне нужно создать синтаксический анализатор строк на С++. Я попытался использовать

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.

Я попробовал использовать токен-код String Toolkit (StrTk) С++

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 +

3Y

Ответ 1

Условие выхода из цикла прерывается:

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

Разрешает ввод с, например pos = npos и startpos = 1.

So

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));
    }

Ответ 2

Что, вероятно, происходит, это сбой при передаче 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);

Ответ 3

Я создал небольшую функцию, которая разбивает строку на подстроки (которые хранятся в векторе), и позволяет вам указать, какие символы вы хотите рассматривать как пробельные символы. Обычные пробелы по-прежнему будут рассматриваться как пробельные символы, поэтому вам не нужно это определять. Фактически, все, что он делает, превращает символ, который вы определили как пробел, в фактическое пробельное пространство (пробел char ''). Затем он запускает это в потоке (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;
}

Ответ 4

Было бы здорово, если бы вы могли поделиться некоторой информацией о своей среде. Ваша программа прошла нормально с входным значением 2X на моей Fedora 20 с помощью g++.