fread读取文本文件,shell读取文本文件
大家好,今天小编来为大家解答fread读取文本文件这个问题,shell读取文本文件很多人还不知道,现在让我们一起来看看吧!
程序员的自我修养: fread-C语言是怎样读取文件的
为了效率的考虑,不至于频繁调用系统函数和访问IO设备,MSVC CRT的fread采用缓冲设计. C语言提供的关于缓冲的函数有:int flush(FILE* stream); int setvbuf(FILE* stream, char* buf, int mode, size_t size);/*缓冲模式mode有: 1.无缓冲模式 _IONBF 2.行缓冲模式 _IOLBF每收到一个换行符(/n或/r/n),就将缓冲flush掉 3.全缓冲模式 _IOFBF仅当缓冲满时才进行flush*/ void setbuf(FILE* stream, char* buf);等价于(void)setvbuf(stream, buf, _IOBBF, BUFSIZ); fread的调用过程大概是:fread-> fread_s(增加缓冲溢出保护,加锁)-> _fread_nolock_s(循环读取,缓冲)-> _read(换行符转换)-> ReadFile(读取文件)加注释的FILE结构如下:struct _iobuf{ char*_ptr; int _cnt;//剩余未读的字节数 char*_base;//文件的缓冲基址 int _flag;//打开文件的属性 int _file;//打开文件的编号 int _charbuf; int _bufsiz;//文件的缓冲的总的大小 char*_tmpfname;}; typedef struct _iobuf FILE;核心函数_fread_nolock_s(循环读取,缓冲)如下:size_t __cdecl _fread_nolock_s( void*buffer, size_t bufferSize, size_t elementSize, size_t num, FILE*stream){ char*data;/* point inside the destination buffer to where we need to copy the read chars*/当前放进字节的尾部 size_t dataSize;/* space left in the destionation buffer(in bytes)//buffer中剩余字节数*/ size_t total;/* total bytes to read//总共要读的字节数*/ size_t count;/* num bytes left to read//剩下要读的字节数*/ unsigned streambufsize;/* size of stream buffer*/ unsigned nbytes;/* how much to read now*/ unsigned nread;/* how much we did read*/ int c;/* a temp char*//* initialize local vars*/ data= buffer; dataSize= bufferSize; if(elementSize== 0|| num== 0){ return 0;}/* validation*/ _VALIDATE_RETURN((buffer!= NULL), EINVAL, 0); if(stream== NULL|| num>(SIZE_MAX/ elementSize)){ if(bufferSize!= SIZE_MAX){ memset(buffer, _BUFFER_FILL_PATTERN, bufferSize);} _VALIDATE_RETURN((stream!= NULL), EINVAL, 0); _VALIDATE_RETURN(num _bufsiz;} else{/* assume will get _INTERNAL_BUFSIZ buffer*/ streambufsize= _INTERNAL_BUFSIZ;}/* here is the main loop-- we go through here until we're done*/ while(count!= 0){/* if the buffer exists and has characters, copy them to user buffer*/ if(anybuf(stream)&& stream->_cnt!= 0){ if(stream->_cnt< 0){ _ASSERTE(("Inconsistent Stream Count. Flush between consecutive read and write", stream->_cnt>= 0)); stream->_flag|= _IOERR; return(total- count)/ elementSize;}/* how much do we want?(unsigned)count: stream->_cnt; if(nbytes> dataSize){ if(bufferSize!= SIZE_MAX){ memset(buffer, _BUFFER_FILL_PATTERN, bufferSize);} _VALIDATE_RETURN(("buffer too small", 0), ERANGE, 0)} memcpy_s(data, dataSize, stream->_ptr, nbytes);/* update stream and amt of data read*/ count-= nbytes; stream->_cnt-= nbytes; stream->_ptr+= nbytes; data+= nbytes; dataSize-= nbytes;} else if(count>= streambufsize){/* If we have more than streambufsize chars to read, get data by calling read with an integral number of bufsiz blocks. Note that if the stream is text mode, read will return less chars than we ordered.*/ if(streambufsize){/* In 64bit apps size_t is bigger than unsigned*(which is 32bit even in 64 bit machines), so* we need to split the read into INT_MAX chunks* since _read() only support up to _signed_ int*(even though the in parameter is unsigned).*/ if(count> INT_MAX){/* calc chars to read-- the largest multiple of streambufsize* smaller then INT_MAX*/ nbytes=(unsigned)(INT_MAX- INT_MAX% streambufsize);} else{/* calc chars to read--(count/streambufsize)* streambufsize*/ nbytes=(unsigned)(count- count% streambufsize);}} else{ nbytes=(count> INT_MAX)?(unsigned)INT_MAX:(unsigned)count;} if(nbytes> dataSize){ if(bufferSize!= SIZE_MAX){ memset(buffer, _BUFFER_FILL_PATTERN, bufferSize);} _VALIDATE_RETURN(("buffer too small", 0), ERANGE, 0)} nread= _read(_fileno(stream), data, nbytes); if(nread== 0){/* end of file-- out of here*/ stream->_flag|= _IOEOF; return(total- count)/ elementSize;} else if(nread==(unsigned)-1){/* error-- out of here*/ stream->_flag|= _IOERR; return(total- count)/ elementSize;}/* update count and data to reflect read*/ count-= nread; data+= nread; dataSize-= nread;} else{/* less than streambufsize chars to read, so call _filbuf to fill buffer*/ if((c= _filbuf(stream))== EOF){/* error or eof, stream flags set by _filbuf*/ return(total- count)/ elementSize;}/* _filbuf returned a char-- store it*/ if(dataSize== 0){ if(bufferSize!= SIZE_MAX){ memset(buffer, _BUFFER_FILL_PATTERN, bufferSize);} _VALIDATE_RETURN(("buffer too small", 0), ERANGE, 0)}*data++=(char) c;--count;--dataSize;/* update buffer size*/ streambufsize= stream->_bufsiz;}}/* we finished successfully, so just return num*/ return num;}其中,int __cdecl _filwbuf( FILE*str)#endif/* _UNICODE*/{ REG1 FILE*stream=NULL;/* In safecrt, we assume we always have a buffer*/ _VALIDATE_RETURN(str!= NULL, EINVAL, _TEOF);/* Init pointer to _iob2 entry.*/ stream= str; if(!inuse(stream)|| stream->_flag& _IOSTRG) return(_TEOF); if(stream->_flag& _IOWRT){ stream->_flag|= _IOERR; return(_TEOF);} stream->_flag|= _IOREAD;/* Get a buffer, if necessary.*/ if(!anybuf(stream)){#ifndef _SAFECRT_IMPL _getbuf(stream);#else/* _SAFECRT_IMPL*//* In safecrt, we assume we always have a buffer*/ _VALIDATE_RETURN(FALSE, EINVAL, _TEOF);#endif/* _SAFECRT_IMPL*/} else{ stream->_ptr= stream->_base;} stream->_cnt= _read(_fileno(stream), stream->_base, stream->_bufsiz);#ifndef _UNICODE if((stream->_cnt== 0)||(stream->_cnt==-1)){#else/* _UNICODE*/ if((stream->_cnt== 0)||(stream->_cnt== 1)|| stream->_cnt==-1){#endif/* _UNICODE*/ stream->_flag|= stream->_cnt? _IOERR: _IOEOF; stream->_cnt= 0; return(_TEOF);} if(!(stream->_flag&(_IOWRT|_IORW))&&((_osfile_safe(_fileno(stream))&(FTEXT|FEOFLAG))==(FTEXT|FEOFLAG))) stream->_flag|= _IOCTRLZ;/* Check for small _bufsiz(_SMALL_BUFSIZ). If it is small and if it is our buffer, then this must be the first _filbuf after an fseek on a read-access-only stream. Restore _bufsiz to its larger value(_INTERNAL_BUFSIZ) so that the next _filbuf call, if one is made, will fill the whole buffer.*/ if((stream->_bufsiz== _SMALL_BUFSIZ)&&(stream->_flag& _IOMYBUF)&&!(stream->_flag& _IOSETVBUF)){ stream->_bufsiz= _INTERNAL_BUFSIZ;}#ifndef _UNICODE stream->_cnt--; return(0xff&*stream->_ptr++);#else/* _UNICODE*/ stream->_cnt-= sizeof(wchar_t); return(0xffff&*((wchar_t*)(stream->_ptr))++);#endif/* _UNICODE*/}代码中分了三种情况:1)缓冲区不为空此时,把缓冲区中的数据复制到传入的字符数组中. 2)缓冲区为空,需要读取的数据大于缓冲的尺寸此时,直接调用函数_fread把文件中的内容写到传入的字符数组中. 3)缓冲区为空,需要读取的数据不大于缓冲的尺寸此时,调用函数_fread读满缓冲区,并再写缓冲区的一个字符到传入的字符数组中.若未读满传入的字符数组,循环执行上述1~3过程,直到读满或读到文件末尾(EOF).
fread大概只能读10k
错误原因:
你用文本方式打开了二进制文件
文本方式读取二进制数据,可能在文件结束之前将某段数据判定为文件末尾EOF,所以结束读取(举个例子,比如遇到 0x00 0x00 0xff 0xff,则文本方式方式的文件流,认为已经到文件末尾,不能读取)
你这个38016的文件,大概在10k左右有段数据和文件结束标志格式相同,文本方式读取到10k左右就认为文件结束了(真正的文本文件,结束标志可能在磁盘簇的剩余空间中)
所以第一种方式:
固定读取38016次,每次往新文件中写一个字节;前10k次能读取到内容, fread返回值是1,这样写过去的一字节就是读取的字节;后28k因为读取失败, fread返回值为0,这样temp的内容就不会被改写,仍然是最后一次成功读取的值,但因为是写次数固定,所以后28k就重复写过去;
后一种方式:
根据fread的返回值来判定文件结束,这是正确的方法;所以读取到10k后,返回值为0,表示无效,文件结束,所以只复制了10k内容
CFile只支持二进读写,所以你的结果是正确的( CFile用CFile::typeText格式会报错; CStdioFile才能文本读写)
用fopen返回的FILE,如果读取的时候没有加b(比如"r"),则默认的是文本格式;所以请用"rb"来读取二进制文件,用"wb"写二进制文件;当然如果只是复制文件的话,纯二进制读写没有问题
下面是楼主要的效果,是一个字节读写的
#include<stdio.h>
int main()
{
FILE*pFileS= fopen("s.rar","rb");
if(! pFileS)
return 1;
FILE*pFileD= fopen("d.rar","wb+");
unsigned char bTemp;
while( fread(&bTemp, sizeof(unsigned char), 1, pFileS))
fwrite(&bTemp, sizeof(unsigned char), 1, pFileD);
fclose( pFileS), fclose( pFileD);
return 0;
}
其实一个字节读写的话,用fgetc和fputc就可以了,当然还是得以二进制方式打开
另外单字节读写速度太慢;系统中复制文件都是整块读写的,设置缓冲大小
比如
#include<stdio.h>
int main()
{
FILE*pFileS= fopen("s.rar","rb");
if(! pFileS)
return 1;
FILE*pFileD= fopen("d.rar","wb");
unsigned char buffer[ 4* 1024 ];
int nRead;
while( nRead= fread( buffer, sizeof(unsigned char), sizeof(buffer), pFileS))
fwrite( buffer, sizeof(unsigned char), nRead, pFileD);
fclose( pFileS), fclose( pFileD);
return 0;
}
另外, fread单次读取的总字节数有限制,也就是说缓冲有上限;只能通过提高次数来读取大文件;在这方面,用API如ReadFile或者调用了这些API的封装类就好得多;当然,次数多对电脑来说不是问题呵呵
附:
至于文本方式不能完全读取,而二进制方式能的原因-
文本方式读取文件,最主要的用处是一次读取一整句(以换行符'\n',即二进制的换行标志"\r\n"结束),方便用于特殊用处ReadString、fscanf(...,"%s",...)之类,每次读取的内容长度是不定的;而二进制读取方式Read、fread等,都是读取固定长度
所以文本方式读取对EOF的判定,是一个文件尾结束标志,如果是文本文件,则这个文件尾肯定不会出现在文件内容中(因为是不可打印字符构成的结束标志,人可读的文本文件不会包括它),这样以结束标志为文件尾则是可以的;二进制文件内容可以是任意字节,如果把它当文本文件来读,以文件尾为结束,当然可能出现把文件内容判定为文件尾的情况;
二进制读取方式由于每次读取固定字节,所以只需要用总文件长度(这个数值是系统管理的数值,不是计算得出来的)减去每次读取的长度(或根据Seek的位置计算长度),就可以知道是否到文件尾,不需要定义结束标志;所以用二进制方式打开任何文件都是合理的
关于c语言读取二进制文件fread问题
这个很正常,这是操作系统的缓存(cache)在起作用。
缓存:为了解决CPU速度和内存速度的速度差异而产生(CPU运算速度比内存快的多)
当程序需要读取某个文件时,实际就是把数据读入内存由CPU运算,CPU首先去缓存中找寻,查找不到就会到内存中去读取同时复制到缓存中以便下次访问,这个时候速度自然很慢,当你第二次读取该文件时,缓存中已经存在,CPU再次访问这些数据就会变的非常快。
这是和系统读取数据的方式有关的,并不是因为某个函数效率低的原因,比较明显的:
你在某个磁盘下搜索某一个文件名,第一次会比较慢,第二次就会快很多,原因就是第二次CPU需要处理的数据已经存在缓存中,处理时效率会非常高。
关于本次fread读取文本文件和shell读取文本文件的问题分享到这里就结束了,如果解决了您的问题,我们非常高兴。