检查流是否以换行符结束
Check if a stream ends with a newline
我想检查一个流(实际上是ifstream)是否以换行符结束。我想到了这个:
bool StreamEndsWithNewline(std::basic_istream<char> & the_stream)
{
if (the_stream.peek() == EOF) {
the_stream.clear(); //clear flags set by peek()
return false;
}
std::string line = "blah";
while (std::getline(the_stream, line)) {
// ...
}
return line.empty();
}
这个想法是,如果流的最后一行有一个结束字符n
, while循环将进行一次额外的迭代(因为没有达到eof),其中空字符串将被分配给line参数。
必须单独处理"空"流的特殊情况。
它似乎在windows (vs2010)上工作。我可以这样做吗?
你的代码工作了。
但是,您可以尝试查找流并只测试最后一个字符或丢弃读取的字符:
#include <cassert>
#include <iostream>
#include <limits>
#include <sstream>
bool StreamEndsWithNewline(std::basic_istream<char>& stream) {
const auto Unlimited = std::numeric_limits<std::streamsize>::max();
bool result = false;
if(stream) {
if(std::basic_ios<char>::traits_type::eof() != stream.peek()) {
if(stream.seekg(-1, std::ios::end)) {
char c;
result = (stream.get(c) && c == 'n');
stream.ignore(Unlimited);
}
else {
stream.clear();
while(stream && stream.ignore(Unlimited, 'n')) {}
result = (stream.gcount() == 0);
}
}
stream.clear();
}
return result;
}
int main() {
std::cout << "emptyn";
std::istringstream empty;
assert(StreamEndsWithNewline(empty) == false);
std::cout << "empty_linen";
std::istringstream empty_line("n");
assert(StreamEndsWithNewline(empty_line) == true);
std::cout << "linen";
std::istringstream line("Linen");
assert(StreamEndsWithNewline(line) == true);
std::cout << "unterminated_linen";
std::istringstream unterminated_line("Line");
assert(StreamEndsWithNewline(unterminated_line) == false);
std::cout << "Please enter ctrl-D: (ctrl-Z on Windows)";
std::cout.flush();
assert(StreamEndsWithNewline(std::cin) == false);
std::cout << 'n';
std::cout << "Please enter Return and ctrl-D (ctrl-Z on Windows): ";
std::cout.flush();
assert(StreamEndsWithNewline(std::cin) == true);
std::cout << 'n';
return 0;
}
;是的,这是保证工作的,除非流最初是空的。
有两个位需要考虑:fail
位和eof
位。std::getline
does, from [string.io]:
在构造一个
sentry
对象之后,如果sentry转换为true,调用str.erase()
,然后从is中提取字符并将它们附加到str
,就像调用str.append(1, c)
[…]如果函数没有提取字符,它调用is.setstate(ios::failbit)
和sentry
一样,from [istream::sentry]:
效果:如果
is.good()
是false
,则调用is.setstate(failbit)
。否则,准备格式化或未格式化的输入。[…如果is.rdbuf()->sbumpc()
或者is.rdbuf()->sgetc()
返回traits::eof()
,函数调用setstate(failbit | eofbit)
那么给出所有这些,让我们看两个例子:
案例1:"hellon"
。第一次调用getline()
, the_stream.good()
为true,我们从n
中抽取字符,流仍然是good()
,我们进入循环体,将line
设置为"hello"
。
第二次调用getline()
时,流仍然是good()
,因此sentry
对象转换为true,我们调用str.erase()
。尝试提取后续字符失败,因为我们已经完成了流,所以设置了failbit
。这将导致返回getline()
转换为false,因此我们不会第二次进入循环体。在循环结束时,line
为空。
Case 2: "goodbye"
,无换行符。第一次调用getline()
, the_stream.good()
为true,我们提取字符直到到达eof()
。流failbit
还没有设置,所以我们仍然进入循环体,行设置为"goodbye"
。
第二次调用getline()
, sentry
对象的构造失败,因为is.good()
为假(is.good()
同时检查eofbit
和failbit
)。由于这个失败,我们没有进入getline()
的第一步,它调用str.erase()
。由于这个失败,设置了failbit
,因此我们再次不进入循环体。
在循环结束时,line
仍然是"goodbye"
。
案例3:""
。这里,getline()
将不提取任何字符,因此设置了failbit
,并且永远不会进入循环,并且line
始终为空。有几种方法可以将这种情况与情况1区分开来:
- 在做其他事情之前,您可以预先设置
peek()
,以查看第一个字符是否为traits::eof()
。 - 你可以计算你进入循环的次数,并检查它是否为零。
- 您可以将
line
初始化为某个哨兵非空值。在循环结束时,只有流以分隔符结束时该行才为空。
- 如何防止clang格式在流运算符调用之间添加换行符<<
- C/C++ - 查询平台相关的换行符(用于内存映射文件)
- 在 Stream C++ 文本之前有一个额外的换行符
- 流:CSV 文件中的换行符
- 如何在三元条件运算符中添加换行符和连接? :在 C++ 中
- Clang-格式:在多行语句之后的换行符上打开大括号
- 在输出流中插入换行符
- 防止控制台在通道字符(在 c++ 中)中输入空格(即空格、制表符和换行符)
- QProcess::readAllStandardOutput() 和换行符
- 如果有换行符,clang 格式不会附加大括号
- 提取后返回换行符的C++istream.get()
- 如何在出现换行符之前将多个整数作为输入?
- 正则表达式 获取两个换行符之间的文本
- 包含换行符分隔的单词的文件和C++中这些单词的字符串向量的大小是否相同?
- 在字符串中输入换行符
- 从文本文件中读取数据并删除所有换行符空格,并在 C++ 控制台中显示
- 读取带引号的 CSV 数据,不带换行符作为结束行
- 检查流是否以换行符结束
- Boost::asio::async_read返回文件结束错误的换行符
- 用换行符结束数组输入