首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >获取std ::ifstream来处理LF、CR和CRLF?

获取std ::ifstream来处理LF、CR和CRLF?
EN

Stack Overflow用户
提问于 2011-05-23 00:29:05
回答 7查看 62.2K关注 0票数 90

具体来说,我对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‘。

代码语言:javascript
复制
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文件分叉。

代码语言:javascript
复制
if(!line.empty() && *line.rbegin() == '\r') {
    line.erase( line.length()-1, 1);
}
EN

回答 7

Stack Overflow用户

回答已采纳

发布于 2011-05-23 00:58:38

正如尼尔所指出的,“C++运行时应该正确地处理您的特定平台的任何行结束约定。”

然而,人们确实会在不同的平台之间移动文本文件,因此这是不够的。下面是一个处理所有三个行尾("\r“、"\n”和“\r\n”)的函数:

代码语言:javascript
复制
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;
        }
    }
}

下面是一个测试程序:

代码语言:javascript
复制
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;
}
票数 116
EN

Stack Overflow用户

发布于 2011-05-23 00:37:51

C++运行时应该正确处理特定平台的终结线约定。具体地说,这段代码应该在所有平台上运行:

代码语言:javascript
复制
#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等):

代码语言:javascript
复制
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;
}
票数 10
EN

Stack Overflow用户

发布于 2012-06-28 17:53:27

您是以二进制模式还是以文本模式读取文件?在文本模式下,成对的回车/换行符CRLF被解释为文本行尾或行尾字符,但在二进制模式下,您一次只能提取一个字节,这意味着必须忽略这两个字符中的任何一个,并将其留在缓冲区中作为另一个字节获取!回车在打字机中,是指打字机的打字机机架,即打印臂所在的位置,已到达纸张的右边缘,并回到左边缘。这是一个非常机械的模型,机械打字机的模型。然后,换行意味着纸卷稍微向上旋转,这样纸张就可以开始另一行输入了。据我所知,ASCII中的一个低位数字表示向右移动一个字符而不打字,即死字符,当然,\b表示退格:将汽车向后移动一个字符。这样,您就可以添加特殊效果,如下划线(下划线)、删除线(减号)、近似不同的重音、取消(X类型),而无需扩展键盘,只需在输入换行符之前调整汽车沿线的位置即可。因此,您可以使用字节大小的ASCII电压来自动控制打字机,而无需计算机。当引入自动打字机时,automatic意味着一旦您到达纸张的最远边缘,小车将返回到左侧并应用换行符,也就是说,假设小车在卷筒向上移动时自动返回!因此,您不需要两个控制字符,只需要一个、\n、换行符或换行符。

这与编程无关,但是ASCII更老了,嘿!看起来有些人在开始做文字的事情时根本没有想过!UNIX平台采用电动自动打字机;Windows模型更完整,允许控制机械机器,尽管一些控制字符在计算机中变得越来越不有用,如钟形字符,0x07,如果我没记错的话……一些被遗忘的文本最初肯定是用电控打字机的控制字符捕获的,它使模型永久化……

实际上,正确的变化应该是只包括\r换行符,不需要回车符,即自动回车,因此:

代码语言:javascript
复制
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文本文件...其转换成双行文本,就像一些印刷文本消费者仍然需要的那样。

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6089231

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档