【问题标题】:C copy file contents from EOF to SOFC将文件内容从EOF复制到SOF
【发布时间】:2013-10-22 16:12:02
【问题描述】:

我的程序几乎可以正常运行。预期目的是从末尾读取文件并将内容复制到目标文件。然而更让我困惑的是lseek() 方法,所以我应该如何设置偏移量。

我的src目前的内容是:
1号线
2号线
3号线

目前我在目标文件中得到的是:
3号线
2
e 2...

据我了解,调用int loc = lseek(src, -10, SEEK_END); 会将源文件中的“光标”移动到末尾,然后将其从 EOF 偏移到 SOF 10 个字节,并且 loc 的值将是我扣除偏移后的文件大小.然而,经过 7 小时的 C 学习后,我几乎脑死了。

int main(int argc, char* argv[])
{
    // Open source & source file
    int src = open(argv[1], O_RDONLY, 0777);
    int dst = open(argv[2], O_CREAT|O_WRONLY, 0777);

    // Check if either reported an erro
    if(src == -1 || dst == -1)
    {
        perror("There was a problem with one of the files.");
    }

    // Set buffer & block size
    char buffer[1];
    int block;

    // Set offset from EOF
    int offset = -1;

    // Set file pointer location to the end of file
    int loc = lseek(src, offset, SEEK_END);

    // Read from source from EOF to SOF
    while( loc > 0 )
    {
        // Read bytes
        block = read(src, buffer, 1);

        // Write to output file
        write(dst, buffer, block);

        // Move the pointer again
        loc = lseek(src, loc-1, SEEK_SET);
    }

}

【问题讨论】:

  • 当然,整个方法仅适用于固定大小的记录,而您的示例数据看起来像一个文本文件,其中每一行的大小可能不同。 (更不用说,即使行是固定长度的,由于换行符和空格,您的 -5 偏移量也是错误的。)
  • @Kevin 我知道,我的解决方案是简单地逐字节读取,但这会导致正确的输出,但是每个单词都被颠倒了,所以我会得到 3eniL ...等
  • 您可以使用 linux.die.net/man/3/strrchr 执行此操作 while (NULL != tmp=strrchr(buffer, '\n')) write(dst,tmp, strlen(tmp));

标签: c unistd.h lseek


【解决方案1】:

lseek() 不会更改或返回文件大小。它返回的是“光标”设置的位置。所以当你打电话时

loc = lseek(src, offset, SEEK_END);

两次它总是将光标再次设置到相同的位置。我猜你想做这样的事情:

while( loc > 0 )
{
    // Read bytes
    block = read(src, buffer, 5);

    // Write to output file
    write(dst, buffer, block);

    // Move the pointer again five bytes before the last offset
    loc = lseek(src, loc+offset, SEEK_SET);
}

如果行长是可变的,您可以改为执行以下操作:

// define an offset that exceeds the maximum line length
int offset = 256;
char buffer[256];
// determine the file size
off_t size = lseek( src, 0, SEEK_END );
off_t pos = size;
// read block of offset bytes from the end
while( pos > 0 ) {
    pos -= offset;
    if( pos < 0 ) {
        //pos must not be negative ...
        offset += pos;   // in fact decrements offset!!
        pos = 0;
    }
    lseek( src, pos, SEEK_SET );
    // add error checking here!!
    read(src, buffer, offset );
    // we expect the last byte read to be a newline but we are interested in the one BEFORE that
    char *p = memchr( buffer, '\n', offset-1 );
    p++;  // the beginning of the last line
    int len = offset - (p-buffer);  // and its length
    write( dst, p, len );
    pos -= len;            // repeat with offset bytes before the last line
}

【讨论】:

  • 我仍然有同样的问题,只是现在我得到了 eLine3
  • 抱歉,第二个lseek() 必须与loc+offset 一起使用,因为offset 是否定的(已编辑)
  • 顺便说一句。 “Line”和“3”之间有空格吗?如果是这样,由于换行符,偏移量必须为 -6
  • 问题是我文件中的每一行长度不同,所以唯一可行的选择是偏移量和缓冲区一次为 1。
  • 如果你看看我是如何改变我的方法的,这是可行的。但是输出是: 3eniL2eniL1eniL 都在同一行,单词之间没有空格。
【解决方案2】:

从您的某些 cmets 看来,您希望反转文本文件中 的顺序。不幸的是,你不会用这样一个简单的程序来做到这一点。您可以采用多种方法,具体取决于您想要获得的复杂程度、文件有多大、手头有多少内存、您希望它有多快等等。

下面是一些不同的想法:

  • 一次将整个源文件读入单个内存块。扫描内存块向前寻找换行符并记录每行的指针和长度。将这些记录保存到堆栈中(您可以使用动态数组或 C++ 中的 STL 向量),然后写入输出文件,只需将行的记录从堆栈中弹出(向后移动数组)并写入直到堆栈为空(您已到达数组的开头。)

  • 从输入文件的末尾开始,但对于每一行,逐个字符向后查找,直到找到开始 上一个行的换行符。越过该换行符再次向前搜索,然后读入该行。 (您现在应该知道它的长度了。)或者,您可以在缓冲区中构建反转的字符,然后将它们反向写出。

  • 一次拉入文件的整个块(可能是扇区),从头到尾。在每个块中,以与上述方法类似的方式定位换行符,除了现在您已经在内存中拥有字符,因此不需要进行任何反转或冗余地拉入它们。但是,此解决方案会复杂得多,因为线条可以跨越块边界。

可能有更复杂/更聪明的技巧,但这些是更明显、更直接的方法。

【讨论】:

    【解决方案3】:

    我认为你应该在最后一次调用lseek() 时使用SEEK_CUR 而不是SEEK_END

    // Set file pointer location to the end of file
    int loc = lseek(src, offset, SEEK_END);
    
    // Read from source from EOF to SOF
    while( loc > 0 )
    {
        // Read bytes
        block = read(src, buffer, 5);
    
        // Write to output file
        write(dst, buffer, block);
    
        // Move the pointer again
        lseek(src, -10, SEEK_CUR);
    }
    

    你也可以这样做:

    // Set file pointer location to the end of file
    int loc = lseek(src, offset, SEEK_END);
    
    // Read from source from EOF to SOF
    while( loc > 0 )
    {
        // Read bytes
        block = read(src, buffer, 5);
    
        // Write to output file
        write(dst, buffer, block);
    
        // Move the pointer again
        loc -= 5;
        lseek(src, loc, SEEK_SET);
    }
    

    【讨论】:

    • 使用 SEEK_CUR 没有帮助,因为 read( ..., 5 )。它还会将位置设置为之前的位置
    • 嗯,这取决于你通过什么。只需向后跳 10 个字节而不是 5 个。
    猜你喜欢
    • 2022-11-24
    • 2018-05-10
    • 2020-11-10
    • 2020-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-11
    • 2021-12-16
    相关资源
    最近更新 更多