cp一个含有空洞的文件

apue 3rd exercises4.6

最近看apue遇到一个很有意思的习题,让我们实现自己的cp函数,他可以copy一个含有空洞的文件但是并不将字节0写到输入文件中去,我开始也并没有思路仅仅,之后Google之后看到一个人用的是方法是一次读一个字节,然后判断字节是否为0,如果不是0就写入到输入出文件中去,如果是0就不写入到文件中去, 我个人觉得这个方法并不靠谱,因为实际文件中很有可能存在一大段字节中有不少字节为0的情况,所以这种方式的cp未免太过简单.之后又看到一个觉得靠谱的思路,也就是我下面要介绍的思路.

  • 先说下思路,主要利用的是新版本下linux支持的lseek()函数的特殊用法,所以不好意思啦,mac os估计是无法使用我的代码了.

    off_t lseek(int fd, off_t offset, int whence);
    Since version 3.1, Linux supports the following additional values for whence:
    SEEK_DATA :Adjust the file offset to the next location in the file greater than or equal to offset containing data. If offset points to data, then the file offset is set to offset.
    SEEK-HOLE :Adjust the file offset to the next hole in the file greater than or equal to offset. If offset points into the middle of a hole, then the file offset is set to offset. If there is no hole past offset, then the file offset is adjusted to the end of the file (i.e., there is an implicit hole at the end of any file).

    以上是官方手册给出的对lseek()函数的解释,很显然,SEEK_DATA,SEEK_HOLE就是我们在代码中需要用到的部分了

  • 第二点就是关键的几个地方,第一个是对初始情况的处理,因为开始我们并不知道文件的开始部分到底是空洞还是数据,所以我们需要先进行处理

    1
    2
    3
    4
    currentPosition = lseek(sourcefd, currentPosition, SEEK_DATA);
    lseek(targetfd, currentPosition, SEEK_SET);
    holePosition = lseek(sourcefd, currentPosition, SEEK_HOLE);
    lseek(sourcefd, currentPosition, SEEK_SET);

    这个算是我们这个cp函数的核心了,我来简单解释下这几行代码的行为

    • 第一行是修改文件偏移量使之指向下一块数据块,新的文件偏移量要大于等于第二个参数代表的偏移量.如果我们在初始情况下调用,currentPosition为0,如果文件开始处是空洞,currentPosition就会指向下一个数据块的起始处.如果文件开始处就是数据块,那么currentPosition仍然会是0.如果在完成字符拷贝之后调用该函数,由于已经完成字符拷贝,所以未调用函数前currentPosition指向是空洞的开始处,调用成功之后currentPosition就会指向我们需要拷贝的下一个数据块开始处咯.
    • 第二行主要是设置目标文件的偏移量,很简单,但是不能忘记设置,否则空洞就全部消失咯.
    • 第三行和第一行的思路是差不多了,理解了第一行的话这一行也不难理解,不过值得一提的是这里面lseek()的第二个参数用的是currentPosition,这样我们才能成功的得到下一个空洞的位置,实际上这个逻辑也是环环相扣的.
    • 第四行和第二行的思路是差不多的,我们设置源文件的文件偏移量为currentPosition,即为我们想要读的数据块的开始处.
  • 第三个就是我们编译的时候需要注意的问题,gcc默认编译的情况下我们会得到SEEK_HOLE和SEEK_DATA未定义的错误,这个时候我们需要带上-D_GNU_SOURCE选项,这样编译的时候就不会报错了

  • 最后贴上代码的链接,我放在我的github上了,另外还有配套的test.c文件来产生有空洞的test.txt文件.codeofapue