在很多人的代码中,我们会发现很多这样的代码
char buf[ 1024 ];
int len = 0;
...
fread( buf, len, sizeof( char ), file1 );
...
fwrite( buf, len, sizeof( char ), file2 );
在正常情况下,这些代码能够正常的运行,但是在某些特殊点程序将出现错误。
我们知道fread和fwrite能够被信号中断,如果信号中没有设置SA_RESTART,那么结果无法确定。
那么如何是上面的操作更加的强壮呢?下面我简单的演示一下实现的方式,希望大家日后尽量使用这种方式来进行读写:-)。
size_t loaded = fread( buf, len, sizeof( char ), file1 );
然后按照loaded来确定真正的读入字节。有些网友可能喜欢这么写
while ( (loaded = fread( buf, len, sizeof( char ), file1 )) > 0 )
{
// do something
}
其实犯的错误也是忽略了信号对fread函数的影响,在信号存在下,loaded可能是0。所以我们应该最好检测是否是中断引起的问题。
while ( !feof( file1 ) )
{
loaded = fread( buf, len, sizeof( char ), file1 );
//do something
}
对于fwrite的使用,也是类似的方法。
for ( int off = 0; off < len; )
{
off += (int) fwrite( buf + off, len - off, sizeof( char ), file2 );
}
这样做看上去好像有些傻瓜,但是对于程序的强壮而言是非常卓越的,希望大家日后尽量这么做!
本文也将提供两个比较规范的程序实现,希望有所帮助!
size_t readn( void *buf, size_t itemSize, size_t items, FILE *fd )
{
size_t loaded = 0;
size_t count;
// Check parameters
assert( buf != NULL );
assert( itemSize > 0 );
assert( items > 0 );
assert( fd != NULL );
// Load requesting data
while ( (count = fread( buf, itemSize, items, fd )) < items )
{
loaded += count;
if ( ferror( fd ) || feof( fd ) )
return loaded;
buf = ((char *) buf) + itemSize * count;
items -= count;
}
loaded += count;
return loaded;
}
size_t writen( const void *buf, size_t itemSize, size_t items, FILE *fd )
{
size_t saved = 0;
size_t count;
// Check parameters
assert( buf != NULL );
assert( itemSize > 0 );
assert( items > 0 );
assert( fd != NULL );
// Save requesting data
while ( (count = fwrite( buf, itemSize, items, fd )) < items )
{
saved += count;
if ( ferror( fd ) )
return saved;
buf = ((char *) buf) + itemSize * count;
items -= count;
}
saved += count;
return saved;
}
鉴于网友的讨论,我觉得下面的方法在某些情况下可能会更好一些,但是他有一个很重要的问题,就是无法通过中断信号来将读写阻塞调用中止。从我个人角度来看,还是直接使用read和write函数比较直观。特别是在linux或者unix系统中。
size_t readn( void *buf, size_t itemSize, size_t items, FILE *fd )
{
size_t loaded = 0;
size_t count;
// Check parameters
assert( buf != NULL );
assert( itemSize > 0 );
assert( items > 0 );
assert( fd != NULL );
// Load requesting data
while ( (count = fread( buf, itemSize, items, fd )) < items )
{
loaded += count;
if ( feof( fd ) )
return loaded;
if ( ferror( fd ) && errno != EINTR )
return loaded;
buf = ((char *) buf) + itemSize * count;
items -= count;
}
loaded += count;
return loaded;
}
size_t writen( const void *buf, size_t itemSize, size_t items, FILE *fd )
{
size_t saved = 0;
size_t count;
// Check parameters
assert( buf != NULL );
assert( itemSize > 0 );
assert( items > 0 );
assert( fd != NULL );
// Save requesting data
while ( (count = fwrite( buf, itemSize, items, fd )) < items )
{
saved += count;
if ( ferror( fd ) && errno != EINTR )
return saved;
buf = ((char *) buf) + itemSize * count;
items -= count;
}
saved += count;
return saved;
}
补充一下,在win32系统中,我们一定要定义_MT,否则errno不是多线程安全的。
在unix或者linux系统中最好定义
_LIBC_REENTRANT或者
_REENTRANT宏,这样能够确保errno一定是多线程安全的。当然,在大多数情况下,默认都是多线程安全的。