具体来说,我对istream& getline ( istream& is, string& str );
感兴趣。ifstream构造器有没有一个选项来告诉它在幕后将所有换行编码转换为'\n‘?我希望能够调用getline
,并让它优雅地处理所有行的结尾。
更新:为了澄清,我希望能够编写几乎在任何地方编译的代码,并且可以从几乎任何地方接受输入。包括包含'\r‘而不包含'\n’的稀有文件。最大限度地减少对任何软件用户的不便。
解决这个问题很容易,但我仍然很好奇,在标准中,如何灵活地处理所有文本文件格式。
getline
将最多为'\n‘的整行内容读入字符串。“\n”是从流中使用的,但getline不将其包含在字符串中。到目前为止还不错,但是在字符串中包含的'\n‘之前可能会有一个'\r’。
在文本文件中可以看到three types of line endings:'\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.
}
编辑感谢尼尔指出f.good()
不是我想要的。!f.fail()
就是我想要的。
我可以自己手动删除它(请参阅此问题的编辑),这对于Windows文本文件来说很容易。但我担心有人会输入一个只包含'\r‘的文件。在这种情况下,我假设getline将消耗整个文件,并认为它只是一行!
。。这甚至没有考虑Unicode :-)
。。也许Boost有一种很好的方法,可以从任何文本文件类型中一次消费一行?
我正在使用这个,来处理文件,但我仍然觉得我不应该这样做!这不会为'\r'-only文件分叉。
if(!line.empty() && *line.rbegin() == '\r') {
line.erase( line.length()-1, 1);
}
发布于 2011-05-23 00:58:38
正如尼尔所指出的,“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;
}
}
}
下面是一个测试程序:
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;
}
发布于 2011-05-23 00:37:51
C++运行时应该正确处理特定平台的终结线约定。具体地说,这段代码应该在所有平台上运行:
#include <string>
#include <iostream>
using namespace std;
int main() {
string line;
while( getline( cin, line ) ) {
cout << line << endl;
}
}
当然,如果您正在处理来自另一个平台的文件,那么所有的赌注都是错误的。
由于两个最常见的平台(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;
}
发布于 2012-06-28 17:53:27
您是以二进制模式还是以文本模式读取文件?在文本模式下,成对的回车/换行符CRLF被解释为文本行尾或行尾字符,但在二进制模式下,您一次只能提取一个字节,这意味着必须忽略这两个字符中的任何一个,并将其留在缓冲区中作为另一个字节获取!回车在打字机中,是指打字机的打字机机架,即打印臂所在的位置,已到达纸张的右边缘,并回到左边缘。这是一个非常机械的模型,机械打字机的模型。然后,换行意味着纸卷稍微向上旋转,这样纸张就可以开始另一行输入了。据我所知,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);
...
将是处理所有类型文件的最正确的方式。但请注意,在文本模式下,\n实际上是字节对0x0d 0x0a,但0x0d只是\r:\n在文本模式下包括\r,而不是在二进制模式下,因此\n和\r\n是等效的...或者说应该是。这实际上是一个非常基本的行业混乱,典型的行业惯性,正如惯例所说的CRLF,在所有平台上,然后落入不同的二进制解释。严格地说,只包含0x0d (回车符)作为\n (CRLF或换行符)的文件在文本模式下是错误的(打字机:只需返回汽车并删除所有内容...),并且是非面向行的二进制格式( \r或\r\n表示面向行),因此您不应该作为文本阅读!代码应该会失败,可能会出现一些用户消息。这不仅依赖于操作系统,还依赖于C库实现,这增加了混乱和可能的变化……(特别是对于透明的UNICODE转换层,为混淆的变体添加了另一个清晰点)。
前面的代码片段(机械打字机)的问题是,如果\r (自动打字机文本)后没有\n字符,效率会非常低。然后,它还假设采用二进制模式,强制C库忽略文本解释(区域设置),并提供纯粹的字节。两种模式的实际文本字符应该没有区别,只有控制字符不同,所以一般来说,读取二进制文件比文本模式更好。此解决方案对于独立于C库变体的二进制模式典型Windows OS文本文件是有效的,而对于其他平台文本格式(包括web翻译为文本)是无效的。如果你关心效率,方法是使用函数指针,以任何你喜欢的方式测试\r vs \r\n行控件,然后选择最好的getline用户代码到指针中并从指针中调用它。
顺便说一句,我记得我还找到了一些\r\r\n文本文件...其转换成双行文本,就像一些印刷文本消费者仍然需要的那样。
https://stackoverflow.com/questions/6089231
复制相似问题