我遇到了使用C ++从文件中读取msg的问题。通常人们所做的是创建文件流,然后使用getline()
函数来获取消息。 getline()
函数可以接受一个附加参数作为分隔符,以便它返回由新分隔符分隔的每个“行”,但不是默认的'\ n'。但是,此分隔符必须是char。在我的用例中,msg中的分隔符可能是“| - |”之类的东西,所以我尝试得到一个解决方案,使它接受一个字符串作为分隔符而不是一个字符。
我已经搜索了StackOverFlow,发现了一些有趣的帖子。
Parse (split) a string in C++ using string delimiter (standard C++)
这个给出了使用string::find()
和string::substr()
来解析任意分隔符的解决方案。但是,那里的所有解决方案都假设输入是一个字符串而不是一个流,在我的情况下,文件流数据太大/浪费,不能一次适应内存所以它应该用msg读取msg(或大量的msg at一旦)。
实际上,通读std::getline()
函数的gcc实现,看起来处理大小写分隔符更容易烧焦。因为每次加载一大块字符时,您总是可以搜索分隔符并将它们分开。虽然如果分隔符不止一个字符不同,分隔符本身可能会跨越两个不同的块并导致许多其他角点情况。
不确定是否有其他人在此之前以及如何优雅地处理此类要求。似乎有一个像istream& getNext (istream&& is, string& str, string delim)
这样的标准函数会很好吗?这对我来说似乎是一个普遍的用例。为什么这个不在标准库中,以便人们不再单独实现自己的版本?
非常感谢
答案 0 :(得分:1)
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;
}
答案 1 :(得分: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时才有效,但是对于像你的例子这样的东西它会正常工作。
答案 2 :(得分:0)
似乎最简单的方法是创建类似getline()
的内容:读取分隔符的 last 字符。然后检查字符串是否足够长以用于分隔符,如果是,则它是否以分隔符结束。如果不是,请继续阅读:
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
}