2017-08-01 60 views
4

我遇到了使用C++从文件读取msg的问题。通常人们做的是创建一个文件流,然后使用getline()函数来获取味精。 getline()函数可以接受一个额外的参数作为分隔符,以便它返回由新分隔符分隔的每个“行”,但不是默认的“\ n”。但是,这个分隔符必须是char。在我的用例中,msg中的分隔符可能与“| - |”类似,所以我尝试获得解决方案,以便它接受字符串作为分隔符而不是char。使用任意分隔符从FileStream中读取

我已经搜索了一下StackOverFlow,发现了一些有趣的帖子。 Parse (split) a string in C++ using string delimiter (standard C++) 这一个给出了一个解决方案,使用string::find()string::substr()解析任意分隔符。然而,所有的解决方案假设输入是一个字符串,而不是一个流,在我的情况下,文件流数据太大/浪费,以便一次装入内存,因此它应该通过msg读取msg(或msg中的大部分msg一旦)。

实际上,通过读取gdb实现的std::getline()函数,似乎更容易处理的情况分隔符是一个单字符。由于每次加载大量字符时,都可以搜索分隔符并将它们分开。虽然分隔符不止一个字符是不同的,但分隔符本身可能会跨越两个不同的块并导致许多其他角落案例。

不确定是否有其他人曾经遇到过这种要求,以及您是如何优雅地处理它的。似乎有一个像istream& getNext (istream&& is, string& str, string delim)这样的标准功能会很好吗?这似乎是我的一般用例。为什么不在Standard库中,这样人们就不再单独实现自己的版本了?

非常感谢您

+0

带字符串的getline需要向前看,因此它可能会比较慢。只是猜测。我们需要实现我们自己的定制getline。 – AndyG

+0

有没有优雅的实现。正如你所提到的,lookahead使代码变得复杂。也许FSM是一个优雅的解决方案? –

+0

向前看将是一个简单的FSM哈哈,只是不像正则表达式那么复杂。程序的要点是读取字符,直到达到“定界符”状态,然后将这些字符解析为字符串。如果您只是对“有效”解决方案感兴趣,可以使用“std :: vector”并进行游戏。 “最优”解决方案会稍微困难一些。如果没有人回答,我会写点东西。 – AndyG

回答

0

如果你确定与字节读取字节,你可以建立一个有限状态机的状态转移表实现识别您的停止条件

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

现在你可以这样使用它

int currentState=0; 
int read=0; 
bool done=false; 
while(!done&&(read=<istream>.read())>=0){ 
    if(read>=256){ 
     currentState=0; 
    }else{ 
     currentState=table[currentState][read]; 
    } 
    if(currentState==endState){ 
     done=true; 
    } 
    //do your streamy stuff 
} 
授予如果分隔符是ASCII扩展这仅适用

,但它会正常工作像你的榜样一些事情。

0

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

看来,这是最容易产生类似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 
} 
相关问题