【问题标题】:Why is this function not breaking up this input string?为什么这个函数不分解这个输入字符串?
【发布时间】:2012-04-27 22:17:59
【问题描述】:

我正在尝试使用 C++ 将字符串分解为“符号”以进行进一步的工作。我已经很久没有用 C++ 写过任何东西了,如果这段代码本身有问题,请原谅我。

下面的symbolize() 函数的目的是将字符串(例如“5+5”)分解为字符串vector,例如{"5","+","5"}。它不工作。如果你觉得代码太乱,请提出一种简化的方法。

到目前为止,这是我的代码:

#include <iostream>
#include <string>
#include <vector>
#include <ctype.h>
#include <sstream>

using namespace std;

vector<string> symbolize(string);

int main(int argc, const char * argv[])
{

    string input;
    cin >> input;

    vector<string> symbols;

    symbols = symbolize(input);

    for(int i=0;i<symbols.size();i++){
        cout<<symbols.at(i) << endl;
    }

    return 0;
}


vector<string> symbolize(string input){
    int position = 0;
    char c;
    stringstream s;
    vector<string> symbols;
    enum symbolType {TEXT,OPERATOR}symbolType,charType;

    while(position < input.size()){
        c = input.at(position);
        if(isalnum(c))symbolType = TEXT;
        else symbolType = OPERATOR;
        charType = symbolType;

        while(symbolType == charType){
            s << c;
            position++;
            if(position>=input.length())break;
            c = input.at(position);
            if(isalnum(c)) charType = TEXT;
            else charType = OPERATOR;
        }

        symbols.push_back(s.str());
        s.clear();
    }

    return symbols;
}

感谢观看。

编辑:顺便说一句,我应该提到该函数返回第一个“令牌”,例如“5+5”->“5”

Edit2:我错了。我刚刚尝试了“5+5”,它返回了{"5","5+","5+5"}。但是,它只返回空格前的第一个。很抱歉造成混乱!

Edit3:谢谢大家!对于那些将来可能会遇到此页面的人,这里是说完一切后的代码:

#include <iostream>
#include <string>
#include <vector>
#include <ctype.h>
#include <sstream>

using namespace std;

vector<string> symbolize(string);

int main(int argc, const char * argv[])
{

    string input;
    getline(cin,input);

    vector<string> symbols;

    symbols = symbolize(input);

    for(int i=0;i<symbols.size();i++){
        cout<<symbols.at(i) << endl;
    }

    return 0;
}


vector<string> symbolize(string input){
    int position = 0;
    char c;
    //stringstream s;
    vector<string> symbols;
    enum symbolType {TEXT,OPERATOR}symbolType,charType;

    while(position < input.size()){
        stringstream s;
        c = input.at(position);
        if(isalnum(c))symbolType = TEXT;
        else symbolType = OPERATOR;
        charType = symbolType;

        while(symbolType == charType){
            s << c;
            position++;
            if(position>=input.length())break;
            c = input.at(position);
            if (isspace(c)||c=='\n'){position++; break;}
            if(isalnum(c)) charType = TEXT;
            else charType = OPERATOR;
        }

        symbols.push_back(s.str());
    }

    return symbols;
}

【问题讨论】:

  • 什么是应该返回。从您的最后一条评论中,“它只返回空格前的第一个”,听起来您的抱怨是“5+5 6+6”只解析到空格然后停止。如果是这样,那是因为您只执行“cin >> input”一次,并且读取到空格。
  • 应该对空格进行测试吗?逻辑假设任何不是字母数字的都是运算符。
  • @abarnert 是的,那是我最初的问题。我还可以使用什么其他方式来包含空格?
  • 另外,如果您想要“5”、“+”和“5”而不是“5”、“5+”和“5+5”,问题在于 stringstream.clear () 不会像您认为的那样做。它清除流错误标志。如果您想在每次循环中清除整个内容,最简单的方法是将变量移动到外部 while 循环中。
  • @wallyk 我使用了“运营商”这个名称,因为我想不出更好的术语。但是,是的,这不仅适用于运营商。

标签: c++ tokenize lexer


【解决方案1】:

如果您想阅读整行而不是一个单词,请使用 getline 而不是 operator>>。有关详细信息,请参阅http://www.cplusplus.com/reference/string/getline/,或者将第 14 行更改为“getline(cin, input);”。

另外,如果你想输出“5”、“+”、“5”而不是“5”、“5+”、“5+5”,每次循环都需要重新设置stringstream, clear 不会那样做。解决这个问题的最简单方法是在外循环中声明 stringstream 并摆脱 clear 调用。

【讨论】:

  • 按原样的代码会将空格视为运算符(因为它们是不是 alnum 的字符)。如果您想忽略它们,则需要为此添加逻辑,例如 if (isspace(c)) { position++;继续; }。或者你也可以使用 operator>> 一次读一个单词,但是将它包装在一个循环中。
  • @abarnet 谢谢。但是,我认为您的意思是:if (isspace(c)){position++; break;},因为它应该中断到空间的外循环。我试过了,它有效。
【解决方案2】:

stringstream::clear 不清除字符串缓冲区(仅清除错误状态)。

您可以使用stringstream::str(x) 设置字符串缓冲区,因此s.str(string())s.str("") 而不是s.clear() 将清除字符串缓冲区。

另外,operator&lt;&lt;(istream, ...) 只读取直到空白。

对于阅读,您可以尝试使用:

  • istream::一次读取一个字符;或;
  • std::getline(istream,...) 一次读取一行;或;
  • istream::read 将任意数量的字符读入缓冲区。

http://en.cppreference.com/w/cpp/io/basic_istream

【讨论】:

  • istream::read 可能不是他想要的。它读取到您的缓冲区大小。大概他事先不知道输入的大小,这意味着他将不得不循环读取,并积累一个缓冲区(因为令牌可以跨越读取之间的边界),这要复杂得多。
  • 读入环形缓冲区是最有效的方法,也是最难实现的。我会把它移到列表的末尾。
  • 这仍然使事情变得过于复杂。 istream::getline 仍然需要您分配一个缓冲区,并且只能读取您已分配的字符数。调用 std::getline(istream&, string&) 要简单得多,除非你担心过长的字符串。
  • @AndrewTomazos-Fathomling 很好的答案,谢谢! abarnet 在 cmets 早些时候给出了他的答案,这就是我接受它的原因。但是感谢您的精彩回答!
  • 对不起 std::getline 是我想要推荐的。已更新。
【解决方案3】:

如果您将stringstream s; 移动到第一个while 循环内,您应该可以实现目标。

s.clear()resets the error state flags 用于字符串流,它不像std::string::clear()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-12
    • 2016-01-18
    • 2021-02-16
    • 2017-07-07
    • 2012-06-20
    • 2020-01-10
    • 2017-02-04
    相关资源
    最近更新 更多