【问题标题】:Reading file backwards , c++ ifstream向后读取文件,c ++ ifstream
【发布时间】:2015-04-07 15:40:29
【问题描述】:

我想从一个文件向后读取 - 从结尾到开头。 这行得通,但我不仅想从文件中获取字符,还想在阅读时删除它们。

std::fstream fileA;
fileA.seekg(-1, fileA.end);
int size = fileA.tellg();
for (i = 1; i <= size; i++)
{
    fileA.seekg(-i, fileA.end);
    fileA.get(ch);

    std::cout << ch;
}

有没有办法做到这一点,而不复制内容并在没有我读过的内容的情况下创建一个新文件?

【问题讨论】:

  • “这行得通”:我想知道你是否尝试过 utf8 文件
  • 记事本说它是 UTF 8 没有 BOM 。那有关系吗?这些代码适用于阅读...... @Christophe
  • 您的方法中的问题是多字节 UTF8 字符。我们以小派为例:它的UTF8编码是0xCF 0x80。如果您在输出中写入 0x80 0xCF ,则它是无效的 UTF8 序列。但是对于任何文本,在 windows 下都会出现同样的问题:'\n' 在文件中被编码为 0x0D 0x0A。在文本模式下阅读时,阅读此序列时您只会得到 '\n'。但是使用您的方法,您将首先定位在 0x0A 上,这将为您提供 '\n',然后您将定位在 0x0D 上,这将再次被读取为 '\n'(因为它后面是 0x0A)。所以你会加倍每个换行符。
  • 你看过this吗?
  • @Christophe:这对于 UTF-8 或 UTF-16 来说很容易解决——你可以从值中看出 0x80 是多字节序列的一部分,你可以告诉您何时到达第一个字节。但是,处理组合变音符号要困难得多 - 当您阅读一个代码点时,除了阅读前面的代码点之外,您不知道它是否可以在它之前加上一个组合变音符号。跨度>

标签: c++ fstream


【解决方案1】:

如果不使用herehere 概述的方法之一,这确实是不可能的。如果查看 istream_iterator 你会发现它是一个输入迭代器 (24.6.1)(1)

类模板 istream_iterator 是一个输入迭代器

然后从 (24.2.1)(表 105)

Random Access -> Bidirectional -> Forward -> Input
                                          -> Output

如您所见,输入迭代器是一种限制性更强的前向迭代器,而前向迭代器只能在一个方向上运行。由于这种行为,它们不是从输入流末尾开始并向后走的标准方式

【讨论】:

  • 没有标准的迭代器可以做到这一点并不意味着没有标准的方法可以做到这一点。仍然对两个链接的问题/答案表示赞成。尤其是mmap() 方法值得考虑。
【解决方案2】:

如果您只想将二进制数据以相反的顺序呈现出来,无论其含义如何,您的代码都可以。

一些建议:

    1234563
  • 您还可以考虑在循环中使用与当前位置的相对位置来向后导航,而不是始终走到末尾并从末尾的绝对位置重新定位自己。

这里是微调的代码:

ifstream fileA("test.txt", ios::binary);  // binary data because binary revert
fileA.seekg(-1, ios::end); // position on last char to be read 
char ch; 
for (; fileA.get(ch); fileA.seekg(-2, ios::cur))  // try to read and rewind.  
    std::cout << ch;

但是,您的代码无法读取正确的 UTF8 编码文件,因为多字节序列将被机械还原,并且它们的还原版本是无效的 UTF8:

  • 如果文件中只有 ASCII 字符,这不是问题。
  • 如果 UTF8 一致性对您来说是个问题,您可以考虑一个非常简单的解决方法:如果您读取一个字符 u 其中 (u &amp; 0xC0) == 0x80 ,您必须读取所有前面的字符,直到此条件为假,并以正确的顺序输出字节组(2 到 8 之间)。

这里是怎么做的:

...                           // If UTF-8 must be processed correctly
fileA.seekg(-1, ios::end);
char ch, buft[9]{},*p;
bool mb=false; 
for (; fileA.get(ch); fileA.seekg(-2, ios::cur))
{
    if (mb) {  // if we are already processing a multibyte sequence
        if ((ch & 0xC0) == 0x80 && p!=buft) // still another byte ?
            *--p=ch; 
        else {
            cout <<ch<<p;   // if no other output the current leading char followed by the multibyte encoding that we've identified
            mb=false;      // and multibyte processing is then finished
        }
    }
    else if ((ch & 0xC0) == 0x80) {  // if a new multibyte sequence is identified
        mb =true;      // start its processing
        buft[7]=ch; 
        p=buft+7; 
    }
    else std::cout << ch;  // normal chars ar procesed as before.
}

这里是runnable demo

最后一点:从输入流中删除最后一个字节取决于操作系统。您应该查看this SO question 以获取有关如何在 linux/posix 和 windows 上执行此操作的答案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多