Aar*_*aid 79 c++ newline ifstream
具体来说我很感兴趣istream& getline ( istream& is, string& str );.是否有ifstream构造函数的选项告诉它将所有换行编码转换为引擎盖下的'\n'?我希望能够打电话getline并优雅地处理所有行结尾.
更新:为了澄清,我希望能够编写几乎可以在任何地方编译的代码,并且几乎可以从任何地方获取输入.包括'\ r'没有'\n'的稀有文件.最大限度地减少软件用户的不便.
解决这个问题很容易,但我仍然对标准中正确处理所有文本文件格式的方式感到好奇.
getline读取一个完整的行,直到'\n',成为一个字符串.'\n'从流中消耗,但getline不包含在字符串中.到目前为止这很好,但是在'\n'之前可能会有一个'\ r'被包含在字符串中.
有三种类型的行结尾的文本文件中看到:"\n"是Unix机器上的常规结尾,"\ r"是在旧的Mac操作系统使用,Windows使用一对(我认为),"\ r"跟随'\n'.
问题是getline在字符串末尾留下'\ r'.
ifstream f("a_text_file_of_unknown_origin");
string line;
getline(f, line);
if(!f.fail()) { // a non-empty line was read
// BUT, there might be an '\r' at the end now.
}
Run Code Online (Sandbox Code Playgroud)
编辑感谢Neil指出这f.good()不是我想要的.!f.fail()是我想要的.
我可以自己手动删除它(请参阅此问题的编辑),这对于Windows文本文件很容易.但是我担心有人会输入一个只包含'\ r'的文件.在这种情况下,我认为getline将消耗整个文件,认为它是一行!
..那甚至不考虑Unicode :-)
..也许Boost有一种很好的方式从任何文本文件类型一次消耗一行?
编辑我正在使用它来处理Windows文件,但我仍然觉得我不应该这样做!这不会为'\ r'专用文件分叉.
if(!line.empty() && *line.rbegin() == '\r') {
line.erase( line.length()-1, 1);
}
Run Code Online (Sandbox Code Playgroud)
Joh*_*åde 106
正如Neil指出的那样,"C++运行时应该正确处理你的特定平台的任何行结束约定."
但是,人们确实在不同平台之间移动文本文件,因此这还不够好.这是一个处理所有三行结尾的函数("\ r","\n"和"\ r \n"):
std::istream& safeGetline(std::istream& is, std::string& t)
{
t.clear();
// The characters in the stream are read one-by-one using a std::streambuf.
// That is faster than reading them one-by-one using the std::istream.
// Code that uses streambuf this way must be guarded by a sentry object.
// The sentry object performs various tasks,
// such as thread synchronization and updating the stream state.
std::istream::sentry se(is, true);
std::streambuf* sb = is.rdbuf();
for(;;) {
int c = sb->sbumpc();
switch (c) {
case '\n':
return is;
case '\r':
if(sb->sgetc() == '\n')
sb->sbumpc();
return is;
case std::streambuf::traits_type::eof():
// Also handle the case when the last line has no line ending
if(t.empty())
is.setstate(std::ios::eofbit);
return is;
default:
t += (char)c;
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个测试程序:
int main()
{
std::string path = ... // insert path to test file here
std::ifstream ifs(path.c_str());
if(!ifs) {
std::cout << "Failed to open the file." << std::endl;
return EXIT_FAILURE;
}
int n = 0;
std::string t;
while(!safeGetline(ifs, t).eof())
++n;
std::cout << "The file contains " << n << " lines." << std::endl;
return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)
小智 10
C++运行时应该正确处理特定平台的endline约定.具体来说,此代码应适用于所有平台:
#include <string>
#include <iostream>
using namespace std;
int main() {
string line;
while( getline( cin, line ) ) {
cout << line << endl;
}
}
Run Code Online (Sandbox Code Playgroud)
当然,如果您正在处理来自其他平台的文件,则所有投注均已关闭.
由于两个最常见的平台(Linux和Windows)都使用换行符终止行,Windows在其前面带有回车符,您可以检查line上面代码中字符串的最后一个字符,看它是否是\r,如果是在进行特定于应用程序的处理之前将其删除.
例如,您可以为自己提供一个类似于此的getline样式函数(未经测试,仅使用索引,substr等用于教学目的):
ostream & safegetline( ostream & os, string & line ) {
string myline;
if ( getline( os, myline ) ) {
if ( myline.size() && myline[myline.size()-1] == '\r' ) {
line = myline.substr( 0, myline.size() - 1 );
}
else {
line = myline;
}
}
return os;
}
Run Code Online (Sandbox Code Playgroud)
您在BINARY或TEXT模式下读取文件吗?在TEXT模式下,对回车/换行CRLF被解释为TEXT行尾或行尾字符,但在BINARY中,您一次只能获取一个字节,这意味着必须忽略任一字符并保留要作为另一个字节获取的缓冲区!在打字机中,回车装置是打印臂所在的打字机车已经到达纸张的右边缘并返回到左边缘.这是一种非常机械的模型,是机械打字机的模型.然后换行意味着纸卷稍微向上旋转,因此纸张就位以开始另一行打字.正如我记得的那样,ASCII中的一个低位意味着移动到右边的一个字符而没有输入,死角,当然\ b表示退格:将汽车移回一个角色.这样你可以添加特殊效果,如底层(类型下划线),删除线(减去类型),近似不同的重音,取消(类型X),而不需要扩展键盘,只需通过调整汽车沿线的位置输入换行符.因此,您可以使用字节大小的ASCII电压来自动控制打字机,而不需要计算机.当引入自动打字机时,AUTOMATIC意味着一旦到达纸张的最边缘,汽车就会返回到左侧并且应用了换行,也就是说,当滚动向上移动时,假定汽车会自动返回!因此,您不需要两个控制字符,只需要一个,\n,新行或换行符.
这与编程无关,但ASCII更老了,嘿!看起来有些人在开始做文字的时候并没有想到!UNIX平台采用电动自动打字机; Windows模型更完整,允许控制机械机器,虽然一些控制字符在计算机中变得越来越少有用,比如钟形字符,如果我记得很清楚,则为0x07 ...一些被遗忘的文本必须最初是用控制字符捕获的对于电控打字机,它使模型永久化......
实际上正确的变化是只包括\ r,换行,回车是不必要的,即自动,因此:
char c;
ifstream is;
is.open("",ios::binary);
...
is.getline(buffer, bufsize, '\r');
//ignore following \n or restore the buffer data
if ((c=is.get())!='\n') is.rdbuf()->sputbackc(c);
...
Run Code Online (Sandbox Code Playgroud)
将是处理所有类型文件的最正确方法.但请注意,TEXT模式下的\n 实际上是字节对0x0d 0x0a,但0x0d IS只是\ r:\n在TEXT模式下包含\ r \n 而在BINARY中不包含\ r \n \n \n\\n \n\\n \n \n \n \n \n \n \n \n \n等等...或者应该.这是一个非常基本的行业混乱实际上,典型的行业惯性,因为公约说话CRLF的,在所有的平台上,然后落入不同的二进制解释.严格地说,文件,包括ONLY 0X0D(回车)为\n(CRLF或换行),在畸形的TEXT模式(typewritter机:刚才坐车返回和删除一切...),并且是面向非线二进制格式(\ r或\ r \n意味着面向行)所以你不应该读作文本!代码应该失败,可能有一些用户消息.这不仅取决于操作系统,而且还取决于C库的实现,增加了混淆和可能的变化......(特别是对于透明的UNICODE转换层添加了另一个令人困惑的变化的清晰点).
以前的代码片段(机械打字机)的问题是,如果在\ r(自动打字机文本)之后没有\n字符,则效率非常低.然后它还采用BINARY模式,其中C库被强制忽略文本解释(语言环境)并放弃纯粹的字节.两种模式之间的实际文本字符应该没有区别,仅在控制字符中,因此一般来说,读取BINARY优于TEXT模式.此解决方案对于BINARY模式典型的Windows操作系统文本文件非常有效,与C库变体无关,对于其他平台文本格式(包括Web翻译到文本)效率低.如果您关心效率,那么可以使用函数指针,按照您喜欢的方式对\ r vs\r \n \n \n \n \n \n \n对行控件进行测试,然后在指针中选择最佳的getline用户代码并从中调用它它.
顺便说一句,我记得我发现了一些\ r \n \n \n \n文本文件......这就像一些印刷文本消费者仍然需要的那样转换成双行文本.