fseek 地址移动太长,超过long最大表示,怎么办

frollo 2008-07-12 02:12:23

要读一个比较大的文件(>10G),是二进制连续的数据流.用fseek移动指针,发现到移动到第2147483648个字节就错误了,2147483648=0x80000000,我想这个应该表示的是long型的一个负数了,所以指针移动就出错了.那有什么办法能够解决这种大文件指针移动的问题么?

当然,如果只是向后移动的话,可以避免fseek,用fread读下去就没有这个问题,目前也是这么做的.但是现在需要实现在文件里前后的功能,还是需要用到fseek,就不知道怎么办了...


代码如下:

目前的办法

for (j = 0; j < 4000000*600; j++)
fread (&unit, 8, 1, fpin);
可以读到最后


出错的代码
for (j = 0; j < 600; j++)
fseek(fpin, (long)(4000000), 1);
读到j=537就错了,4000000*537 = 2148000000 大于 0x80000000
所以fseek就乱掉了




...全文
998 21 打赏 收藏 转发到动态 举报
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
ChamPagneZ 2008-07-13
  • 打赏
  • 举报
回复
mark来学习一下
mymtom 2008-07-13
  • 打赏
  • 举报
回复
Linux可以用:
int fseeko(FILE *stream, off_t offset, int whence);
mymtom 2008-07-13
  • 打赏
  • 举报
回复
用新的C标准函数啊:
int fsetpos(FILE *stream, const fpos_t *pos);
hurry281 2008-07-13
  • 打赏
  • 举报
回复
用大一点的数据避免数据益处,或者对文件分割
e_bot 2008-07-13
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 akirya 的回复:]
C/C++ code
int _fseeki64(
FILE *stream,
__int64 offset,
int origin
);
[/Quote]
asmst 2008-07-13
  • 打赏
  • 举报
回复
楼主的这个问题我遇到过,我这里给出2个建议,供楼主参考:
1.32位的指针一次可以移动2^31-1,那么把位移表示成这个数值的N倍,再加上一个余量就行了,不停的在当前位置为基准前提下向前移动,不要检查函数返回值,我的程序就是这样干的,还能正常工作。
2.在linux下已经有了64位的指针,不过你在包含相关头文件之前,要首先定义一个宏
__USE_LARGEFILE64,然后你就可以使用64位的指针了。

以下的代码供参考,来自我的程序源代码,注意我使用的函数是lseek().

int set_offset64(int fd,long long offset)
{
long long i,maxl=0;
if(fd < 0 ) return -EOPEN;
maxl=lseek64(fd,0,SEEK_END);
i=lseek64(fd, offset, SEEK_SET);
/*if i greater than the file size of fd,
return -EEOF and reset the file pointer to
the start of fd.*/
if(i >= maxl) {
lseek64(fd,0,SEEK_SET);
return -EEOF;
}
if(i != offset) return -ESEEK;
return 0;
}

int set_offset32(int fd,long long offset)
{
/*if we can not use 64 bits offset,the largest
size we can move at one time is 2^30. */
#define SHIFT_BITS 30
unsigned long i;
if(fd < 0) return -EOPEN;
i=lseek(fd,0,SEEK_SET);
/*here i must equals 0,if not,error.*/
if(i != 0) return -ESEEK;
for(i=0;i<(long long)offset>>SHIFT_BITS;i++){
/*we can only check the first result of lseek( )*/
if(i==0){
i=lseek(fd,1<<SHIFT_BITS,SEEK_CUR);
if(i != (1<<SHIFT_BITS))
return -ESEEK;
i=0;
}
else lseek(fd,1<<SHIFT_BITS,SEEK_CUR);
}
lseek(fd,(long long)offset%(1<<SHIFT_BITS),SEEK_CUR);
return 0;
}

inline int set_offset(int fd,long long offset)
{
int i;
#ifdef __USE_LARGEFILE64
i=set_offset64(fd,offset);
#else
i=set_offset32(fd,offset);
#endif
return i;
}
JohnHealy 2008-07-12
  • 打赏
  • 举报
回复
楼主,请参看

struct _iobuf {
char *_ptr; // Current active pointer
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;

里的各成员的含义,实在不行的话,看看fseek函数的定义吧。
JohnHealy 2008-07-12
  • 打赏
  • 举报
回复
long 是有符号的32位类型 2147483648=1000 0000 0000 0000 0000 0000 0000 0000 很明显是个负数;

unsigned long 是无符号的32位类型 2147483648=1000 0000 0000 0000 0000 0000 0000 0000 很明显是个正数

但,当>4294967295(0xFF FF FF FF FF FF FF FF)时,也会出现问题;

在看看 size_t fread( void *buffer, size_t size, size_t count, FILE *stream )里的FILE 定义;
struct _iobuf {
char *_ptr; // Current active pointer
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;

所以楼主试试,在stream->_ptr=stream->_ptr+2147483647;
如此循环,再读剩下的内容;




沙漠里的海豚 2008-07-12
  • 打赏
  • 举报
回复
对了楼主,你把j声明为unsigned long 类型的能行吗
沙漠里的海豚 2008-07-12
  • 打赏
  • 举报
回复
呵呵 明白了 谢谢LZ 希望你能尽快解决问题
frollo 2008-07-12
  • 打赏
  • 举报
回复
好吧,那只能这样了,谢akirya了


回ls,书上说,int表示的范围是-32768~32767,应该是有正有负,首位为1当然就是表示负数了,这是补码的定义.unsigned int可以表示0~65535.
long表示的范围是-2e31~(2e31-1).

不过这些个前提是开发的平台是16位的情况下,目前应该都至少是32位了,也就是一个int默认为32位了,所以现在机器上的int和long表示的范围已经默认一致
了.

不过即使这样也不能满足我这个变态的数据的要求...
weiaixinshui 2008-07-12
  • 打赏
  • 举报
回复
这个问题我也遇到过,我现在加了一个群,这个群人气较好,也的确有高手,不但可以交流问题,而且还可以接项目赚钱,QQ群号是:陆@肆@柒@玖@捌@柒@贰@零,不要说我做广告,看看便知,如需要的话加入试试,如果你发现好的交流群也别忘了告诉我啊!呵呵!
沙漠里的海豚 2008-07-12
  • 打赏
  • 举报
回复
我还是不太明白LZ的意思

假设int型数据为16位,那么它能表示的最大整数应该是65535对吧,照你的意思当整数的值等于2的15次方时其二进制表示的第一位也是1,不也成了负数了吗?难道说int只能表示到32767吗?

如果不是的话我觉得你说的出错原因好像就不对了。


我对这方面的知识一直不是理解的特别号,不对之处请楼主指教
  • 打赏
  • 举报
回复
有些时候是标准库无法解决的事情
这个时候你就自己根据平台写两个同名函数来完成他。
frollo 2008-07-12
  • 打赏
  • 举报
回复
呃,继续解释...

没错,long为4个字节,其能表示的最大值是4*1024*1024*1024 - 1,也就是十六进制的0xFFFFFFFF

但是,累计到0x80000000时,二进制首位为1,就表示负数了,补码就是这么来的是吧.现在不是讨论long能表示多长的问题,现在是我的文件太大了,有十几G,32个字节表示不了...

我的那段代码只是表示这个意思...
沙漠里的海豚 2008-07-12
  • 打赏
  • 举报
回复
long为4个字节,其能表示的最大值是4*1024*1024*1024 - 1,这个值应该是4294967296 - 1,肯定大于你的4000000*600=2400000000的啊
你出错的代码是
for (j = 0; j < 600; j++)
fseek(fpin, (long)(4000000), 1);

这个???
frollo 2008-07-12
  • 打赏
  • 举报
回复
查了一下,那个fseeki64是vs2005才有的东东,我现在只能用vc6,而且要在linux上用gcc实现.还有啥办法没.

在强调一下,我要实现的是文件的任意定位,用fread不行啊...
K行天下 2008-07-12
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 frollo 的回复:]
多谢1L,不过我帖子里说了,我现在就是用fread实现的一样的功能.可是我现在有时需要在文件中往回移动,比如fseek(fpin, -(long)(4000000), 1),不知这个如何用fread实现?

多谢2L,不过不明白是啥意思?在程序开头加上这段代码?
[/Quote]
你用long还表示不了吗?
long是四字节啊!!
四字节32位
你的文件多大?
frollo 2008-07-12
  • 打赏
  • 举报
回复
多谢1L,不过我帖子里说了,我现在就是用fread实现的一样的功能.可是我现在有时需要在文件中往回移动,比如fseek(fpin, -(long)(4000000), 1),不知这个如何用fread实现?

多谢2L,不过不明白是啥意思?在程序开头加上这段代码?


  • 打赏
  • 举报
回复

int _fseeki64(
FILE *stream,
__int64 offset,
int origin
);
加载更多回复(1)

69,373

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧