Linux下快速扩展文件大小

161 篇文章 0 订阅
117 篇文章 0 订阅

    在实际的开发过程中,我们可能需要在文件生成时就立即将文件更改为指定的大小,以便于后续简化对文件的并发操作,
这一点在各类的下载工具中有很好的体现。
    要满足这一需求,通常可以使用的最直观的方式是:生成文件后,通过循环向文件中写入任意数据,直到文件大小达到预期的大小。
对于较小的文件,这种方法可以接受,但是如果文件大小过大,那么这种方法就显得不够简洁且低效。
    这里介绍一种高效且简单的方法,基于函数"lseek"。
    执行"man lseek"之后,我们可以看到关于lseek特性的关键信息,如下:
    The lseek() function shall allow the file offset to be set beyond the end of the existing data in the file.
    If data is later written at this point, subsequent reads of data in the gap shall return bytes with the
    value 0 until data is actually written into the gap.
    The lseek() function shall not, by itself, extend the size of a file.

简单的总结就是:
1. lseek 函数本身不会扩展文件的大小.
2. lseek 允许文件的偏移值超过文件的末端,如果下一次在这个偏移点写入数据,那么在偏移之前的文件末端与偏移点之间的数据将会自动填充为0。

了解了上面几点,大家应该想到了扩展文件大小将是一件多么简洁高效的事情了:) 



// ! @brief 扩展文件大小
/*!
    扩展一个已打开的文件的大小
    @note <b>此函数只能用于扩展一个文件的大小,不能缩小一个文件的大小.如果需要缩小一个文件大小可以使用 Linux API "truncate"</b>。
          此函数不会改变文件指针的位置,即如果文件原大小为N,文件指针位置为K( K >= 0 , K <=N ),调用此函数后,文件大小扩展为 M( M>N ),
          那么在调用函数完成后,文件指针仍然位于位置K。
          <b>此函数能且只能"沿着向文件末端的方向扩展文件大小"</b>。
    @param iFileHandle 扩展大小的文件的句柄
    @param iNewSize 扩展后的文件的大小
    @return 如果调用成功,返回<b>true</b>;如果调用失败,返回<b>false</b>。
 
*/

bool  EnlargeFile(  int  iFileHandle , off_t iNewSize )
{
    
if( ( iFileHandle < 0 ) || ( iNewSize < 0 ) )
    
{
        LOG_DEBUG_INFO( 
"parameter for < EnlargeFile( %d , %ld ) > is not valid" , iFileHandle , iNewSize );
        
return false;
    }

    
    
struct stat fileState;
    
int iStatus = fstat( iFileHandle , &fileState );
    
if0 != iStatus )
    
{
        LOG_DEBUG_INFO( 
"invoke < fstat( %d , %p ) > fail , ErrCode:%d , ErrMsg:%s" , \
                        iFileHandle , 
&fileState , errno , strerror( errno ) );
        
return false;
    }

    
    
const off_t iCurSize = fileState.st_size;
    
if( iNewSize <= iCurSize )
    
{
        LOG_DEBUG_INFO( 
"new file size = %ld , current file size = %ld , can not resize file to smaller size." , \
                        iNewSize , iCurSize );
        
return false;
    }


    
const off_t iCurPos = lseek( iFileHandle , 0 , SEEK_CUR );                //保存文件指针的当前位置以便于在扩展文件大小后恢复到当前位置
    if( iCurPos < 0 )
    
{
        LOG_DEBUG_INFO( 
"invoke < lseek( %d , 0 , SEEK_CUR ) > fail , ErrCode:%d , ErrMsg:%s" , \
                        iFileHandle , errno , strerror( errno ) );
        
return false;
    }

    
    
const off_t iMoveOffset = iNewSize - 1;
    iStatus 
= lseek( iFileHandle , iMoveOffset , SEEK_SET );
    
if( iStatus != iMoveOffset )
    
{
        LOG_DEBUG_INFO( 
"invoke < lseek( %d , %ld , SEEK_SET ) > fail , ErrCode:%d , ErrMsg:%s" , \
                        iFileHandle , iMoveOffset ,  errno , strerror( errno ) );
        
return false;        
    }

    
    
const int WRITE_BYTE_COUNT = 1;
    iStatus 
= write( iFileHandle , " " , WRITE_BYTE_COUNT );                //写入一个字节的数据,完成对文件大小的更改
    if( iStatus != WRITE_BYTE_COUNT )
    
{
        LOG_DEBUG_INFO( 
"invoke < write( %d , \" \" , %d ) > fail , ErrCode:%d , ErrMsg:%s" , \
                        iFileHandle , WRITE_BYTE_COUNT ,  errno , strerror( errno ) );
        
return false;        
    }

    
    iStatus 
= lseek( iFileHandle , iCurPos , SEEK_SET );                        //恢复文件指针到之前保存的文件位置
    if( iStatus != iCurPos )
    
{
        LOG_DEBUG_INFO( 
"invoke < lseek( %d , %ld , SEEK_SET ) > fail , ErrCode:%d , ErrMsg:%s" , \
                        iFileHandle , iCurPos ,  errno , strerror( errno ) );
        
return false;    
    }

    
    
return true;
}




点击下载

Linux manpages CHM文件下载:
Linux_manpages_part_1_of_4
Linux_manpages_part_2_of_4
Linux_manpages_part_3_of_4
Linux_manpages_part_4_of_4
(由于cppblog限制单个文件上传大小,这里将CHM分割成4部分,全部下载后解压即可).

如果下载速度太慢,请前往这里下载.

Linux下快速扩展文件大小

 在实际的开发过程中,我们可能需要在文件生成时就立即将文件更改为指定的大小,以便于后续简化对文件的并发操作,
这一点在各类的下载工具中有很好的体现。
    要满足这一需求,通常可以使用的最直观的方式是:生成文件后,通过循环向文件中写入任意数据,直到文件大小达到预期的大小。
对于较小的文件,这种方法可以接受,但是如果文件大小过大,那么这种方法就显得不够简洁且低效。
    这里介绍一种高效且简单的方法,基于函数"lseek"。
    执行"man lseek"之后,我们可以看到关于lseek特性的关键信息,如下:
    The lseek() function shall allow the file offset to be set beyond the end of the existing data in the file. 
    If data is later written at this point, subsequent reads of data in the gap shall return bytes with the 
    value 0 until data is actually written into the gap.
    The lseek() function shall not, by itself, extend the size of a file.

简单的总结就是:
1. lseek 函数本身不会扩展文件的大小.
2. lseek 允许文件的偏移值超过文件的末端,如果下一次在这个偏移点写入数据,那么在偏移之前的文件末端与偏移点之间的数据将会自动填充为0。

了解了上面几点,大家应该想到了扩展文件大小将是一件多么简洁高效的事情了:)  

那么,简单的步骤如下


     
     
1 . lseek( iFileHandle , 0 , SEEK_CUR ); // 保存文件指针的当前位置以便于在扩展文件大小后恢复到当前位置 2 . lseek( iFileHandle , iMoveOffset , SEEK_SET ); 3 . write( iFileHandle , " " , WRITE_BYTE_COUNT ); // 写入一个字节的数据,完成对文件大小的更改 4 . lseek( iFileHandle , iCurPos , SEEK_SET ); // 恢复文件指针到之前保存的文件位置

实际上,在Windows中的_lseek或者_lseeki64也可以达到相同的目的。MSDN中的描述:

You can use _lseek to reposition the pointer anywhere in a file or beyond the end of the file.


最后,还有一个更好的方法:

int truncate(const char *path, off_t length); 
If the file previously was larger than length, the extra data is discarded. 
If the file was previously shorter than length, its size is increased, and the extended area appears as if it were zero-filled. 
这样一来,截断和扩展文件都可以用"truncate"来完成,这样相对于上面的EnlargeFile就更简洁了而且基于"truncate"的形式只需要一次系统调用即可实现相同的效果,效率上也更有优势. 

但是,truncate函数在Windows中不可以使用。可以使用 SetEndOfFile 来代替。

 

Truncating or Extending Files

An application can truncate or extend a file by calling SetEndOfFile on sets the end-of-file marker to the current position of the file pointer.

Note that when a file is extended, the contents between the old and new end-of-file locations are not defined.

 

The SetEndOfFile function can be used to truncate or extend a file. If the file is extended, the contents of the file between the old end of the file and the new end of the file are not defined.

SetEndOfFile 可以用来扩展文件,当一个文件被扩展,被扩展部分,是未被定义的。也就是说,新扩展出来的部分,没有被付初值,没有用0填充。

还需要注意一点,

If CreateFileMapping is called to create a file mapping object for hFileUnmapViewOfFile must be called first to unmap all views and call CloseHandle to close the file mapping object before you can call SetEndOfFile.

也就是说,在Mapping状态的文件,是不能调用SetEndOfFile的。

MSDN链接:http://msdn.microsoft.com/en-us/library/aa365531(VS.85).aspx

 




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值