使用任意分隔符读取文件流 我遇到了一个问题,用C++读取文件中的MSG。通常人们所做的是创建一个文件流,然后使用getline()函数获取消息getline()函数可以接受一个附加参数作为分隔符,以便返回由新分隔符分隔的每个“行”,而不是默认的“\n”。但是,此分隔符必须是字符。在我的用例中,msg中的分隔符可能是其他类似“|--|”的东西,因此我尝试获得一个解决方案,使其接受字符串作为分隔符而不是字符

使用任意分隔符读取文件流 我遇到了一个问题,用C++读取文件中的MSG。通常人们所做的是创建一个文件流,然后使用getline()函数获取消息getline()函数可以接受一个附加参数作为分隔符,以便返回由新分隔符分隔的每个“行”,而不是默认的“\n”。但是,此分隔符必须是字符。在我的用例中,msg中的分隔符可能是其他类似“|--|”的东西,因此我尝试获得一个解决方案,使其接受字符串作为分隔符而不是字符,c++,getline,C++,Getline,我搜索了一下StackOverFlow,发现了一些有趣的帖子。 这个解决方案提供了一个使用string::find()和string::substr()以任意分隔符进行解析的解决方案。然而,所有的解决方案都假设输入是一个字符串而不是一个流,在我的例子中,文件流数据太大/浪费,无法立即放入内存,因此它应该逐个msg(或一次读取大量msg)读取msg 实际上,通读一下std::getline()函数的gcc实现,似乎更容易处理大小写分隔符是单个字符。因为每次加载一个字符块时,都可以搜索分隔符并将其分

我搜索了一下StackOverFlow,发现了一些有趣的帖子。 这个解决方案提供了一个使用
string::find()
string::substr()
以任意分隔符进行解析的解决方案。然而,所有的解决方案都假设输入是一个字符串而不是一个流,在我的例子中,文件流数据太大/浪费,无法立即放入内存,因此它应该逐个msg(或一次读取大量msg)读取msg

实际上,通读一下
std::getline()
函数的gcc实现,似乎更容易处理大小写分隔符是单个字符。因为每次加载一个字符块时,都可以搜索分隔符并将其分隔开。虽然分隔符是多个字符是不同的,但分隔符本身可能跨在两个不同的块之间,并导致许多其他角情况

我不知道以前是否有人遇到过这种需求,你们是如何优雅地处理的。如果有一个标准函数,比如
istream&getNext(istream&is,string&str,string-delim)
,那就太好了?对我来说,这似乎是一个通用的用例。为什么这个版本不在标准库中,这样人们就不再需要单独实现自己的版本了


非常感谢您

如果您可以逐字节读取,您可以构建有限状态机的状态转换表实现来识别停止条件

std::string delimeter="someString";
//initialize table with a row per target string character, a column per possible char and all zeros
std::vector<vector<int> > table(delimeter.size(),std::vector<int>(256,0));
int endState=delimeter.size();
//set the entry for the state looking for the next letter and finding that character to the next state
for(unsigned int i=0;i<delimeter.size();i++){
    table[i][(int)delimeter[i]]=i+1;
}
std::string delimeter=“someString”;
//使用每个目标字符串一行、每个可能的字符一列和全零初始化表
std::vector表(delimeter.size(),std::vector(256,0));
int endState=delimeter.size();
//为查找下一个字母并将该字符查找到下一个状态的状态设置条目
for(无符号整数i=0;i=0){
如果(读取>=256){
currentState=0;
}否则{
currentState=表[currentState][read];
}
如果(currentState==endState){
完成=正确;
}
//做你自己的事
}

当然,这只在定界符是扩展ASCII格式的情况下有效,但对于您的示例这样的事情,它可以正常工作。

STL根本不支持您所要求的内容。您必须编写自己的函数(或找到第三方函数)来满足您的需要

例如,您可以使用
std::getline()
读取分隔符的第一个字符,然后使用
std::istream::get()
读取后续字符并将其与分隔符的其余部分进行比较。例如:

std::istream& my_getline(std::istream &input, std::string &str, const std::string &delim)
{
    if (delim.empty())
        throw std::invalid_argument("delim cannot be empty!"); 

    if (delim.size() == 1)
        return std::getline(input, str, delim[0]);

    str.clear();

    std::string temp;
    char ch;
    bool found = false;

    do
    {
        if (!std::getline(input, temp, delim[0]))
            break;

        str += temp;

        found = true;

        for (int i = 1; i < delim.size(); ++i)
        {
            if (!input.get(ch))
            {
                if (input.eof())
                    input.clear(std::ios_base::eofbit);

                str.append(delim.c_str(), i);
                return input;
            }

            if (delim[i] != ch)
            {
                str.append(delim.c_str(), i);
                str += ch;
                found = false;
                break;
            }
        }
    }
    while (!found);

    return input;
}
std::istream&my_getline(std::istream&input,std::string&str,const std::string&delim)
{
if(delim.empty())
throw std::无效的_参数(“delim不能为空!”);
如果(delim.size()==1)
返回std::getline(输入,str,delim[0]);
str.clear();
std::字符串温度;
char ch;
bool-found=false;
做
{
如果(!std::getline(输入、温度、delim[0]))
打破
str+=temp;
发现=真;
对于(int i=1;i
看起来,创建类似于
getline()
:读取分隔符的最后一个字符是最简单的。然后检查字符串的长度是否足以容纳分隔符,如果足够,则检查字符串是否以分隔符结尾。如果不是,请继续阅读:

std::string getline(std::istream& in, std::string& value, std::string const& separator) {
    std::istreambuf_iterator<char> it(in), end;
    if (separator.empty()) { // empty separator -> return the entire stream
        return std::string(it, end);
    }
    std::string rc;
    char        last(separator.back());
    for (; it != end; ++it) {
        rc.push_back(*it);
        if (rc.back() == last
            && separator.size() <= rc.size()
            && rc.substr(rc.size() - separator.size()) == separator) {
            return rc.resize(rc.size() - separator.size());
        }
    }
    return rc; // no separator was found
}
std::string getline(std::istream&in,std::string&value,std::string const&separator){
std::istreambuf_迭代器it(in),end;
if(separator.empty()){//empty separator->返回整个流
返回std::string(it,end);
}
std::字符串rc;
字符last(separator.back());
for(;it!=end;++it){
rc.推回(*it);
如果(rc.back()==last

&&分隔符大小()带字符串的getline需要前瞻,因此一般来说可能会慢一些。只是猜测。我们需要实现我们自己的自定义getline。是否有优雅的实现。正如您所提到的,前瞻会使代码复杂。也许FSM是一个优雅的解决方案?前瞻将是一个简单的FSM哈哈,只是没有常规的FSM那么复杂表达式。该程序的要点是读入字符,直到达到“分隔符”状态,然后将这些字符解析为字符串。如果您只是对“有效”的解决方案感兴趣,请使用
std::vector
并四处游荡。“最佳”解决方法会更难一些。如果没有人回答,我会写一些东西。我会尝试
std::getline
读取分隔符字符串和缓冲区的第一个字符,直到下一次读取测试是否有分隔符。如果有,如果没有,则存储缓冲区并附加到缓冲区,然后继续。@Ga我也在想同样的事情。
std::string getline(std::istream& in, std::string& value, std::string const& separator) {
    std::istreambuf_iterator<char> it(in), end;
    if (separator.empty()) { // empty separator -> return the entire stream
        return std::string(it, end);
    }
    std::string rc;
    char        last(separator.back());
    for (; it != end; ++it) {
        rc.push_back(*it);
        if (rc.back() == last
            && separator.size() <= rc.size()
            && rc.substr(rc.size() - separator.size()) == separator) {
            return rc.resize(rc.size() - separator.size());
        }
    }
    return rc; // no separator was found
}