作者EdisonX (卡卡兽)
看板C_and_CPP
标题Re: [问题] 如何知道一个档案有几行
时间Sun Feb 2 20:01:32 2014
原文在这
#1DyUBD7Q (C_and_CPP)
http://webptt.com/cn.aspx?n=bbs/C_and_CPP/M.1307697869.A.1DA.html
都翻起旧文了,给个机会,澄清一下。
我先自首件事,这篇文当初我只有小提 fread + strchr 可以达成
较稳定的需求,会这麽说是有个专案 BUF_SIZE 设 32767 , 结果
fgets 档案有一行真的 overflow。
我必须强调,当初讲的 fgets / fread + strchr / fgetc 等方式,
其实都没考虑到档案编码、多国语系问题,然後 fread + strchr 只
是小提一下,当时实作上是顺手写的,这次将它完善。
※ 引述《gary8520 (元丁)》之铭言:
: 小弟学C不久,非资工人,
: 正在写一个小程式需要读数十行,每行字元十个左右的资料。
: 想了解BUF_size大约要怎麽取,
: 小小测试了一下,
: 我使用动态记忆体配制决定BUF_SIZE的大小(用for回圈跑),
: 并算出line_cut。
: ※ 引述《tropical72 (蓝影)》之铭言:
<............恕删............>
: : ----------
: : step 4: 用 fread 进行
: 计算结果输出为line_cnt4
: 这似乎就要看BUF_SIZE的大小…
<............恕删............>
: 只要bufsize一改,这个方法算出来的结果就会不同?
: 说实在话我不知道为什麽,也想不出来为什麽"Orz
: 所以,若要像我读小笔资料,step3的方法似乎是比较适当的。
^^^^^^^^^^^^ 就是 fgets
一般而言,不论档案大小,大多做法都是用 fgets 去做,因很少有机会会遇到
存文字档,一行很长的(正确的说,很少情况会遇到纯文字档会写很大的,写大
的话到後来都是用 binary mode 写入),所以 buf_size 设大一点就没事了,
一般我是直接给
BUFSIZ * 4 ,我手边 compiler BUFSIZ 是给 512 。
---------------------
原 source code
有问题的 重点如下
while(BUF_SIZE==fread(buf, 1, BUF_SIZE, fp)){
ptr = (char*)strchr(buf, '\n');
/* 这里还有个 issue 要修正 */
while(ptr!=NULL){
++line_cnt;
ptr = (char*)strchr(ptr+1, '\n');
}
}
关键其实在於
fread 传回值 ,代表成功从档案读取了几个 bytes ,
原本是只考虑成功读取了 BUF_SIZE bytes 时才继续往下做 , 想一下
如果档案有 351 bytes, 每次读 100 bytes , 最後会有 51 bytes 会
被丢掉,所以判断式不该那麽下
[Lemma],要简单的话是只要 fread
传回值是非 0 就直接往下做。
然後考虑一下最後一次 fread 的情况,假设 BUF_SIZE = 100 , 但只读
了 51 bytes , 这时候
buf 後面的 49 bytes 都不会被清 0 , 意思是说
如果 buf 後 49 bytes 里面有 '\n' 的话就会被重覆计算, 所以在做
string search 之前要再塞个结束字元。
整个可以 run 的 code 如下。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum {LINE_CNT = 150, BUF_SIZE = 20};
const char * FILENAME = "tst.txt";
int main()
{
FILE * fp ;
char * ptr;
char buf[BUF_SIZE+1] ; // +1 : 加上结束字元
size_t read_bytes , line_cnt = 0;
fp = fopen(FILENAME, "rb"); // no error defect
while(read_bytes = fread(buf, 1, BUF_SIZE, fp)) { // read_bytes==0 时结束
buf[read_bytes] = '\0';
ptr = (char*)strchr(buf, '\n');
while(ptr!=NULL) {
++line_cnt;
ptr = (char*)strchr(ptr+1, '\n');
}
}
fclose(fp);
printf("line_cnt = %d\n", line_cnt);
return 0;
}
然後整个 fread + strchr , 其实可用 fread + memchr 做 , memchr 速度应会比
strchr 还快一点点 , 这里就不再示范。
[Lemma]
当初之所以会用
while(BUF_SIZE==fread(buf, 1, BUF_SIZE, fp)) ,
是因为不想在 while loop 里面做很多事,想单纯化,最後没读满 BUF_SIZE
的是跳出 loop 之後再独立做,速度估会较快。
--
~ 这辈子与神手无缘
我只好当神兽了 ~
卡卡兽
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 180.177.74.188
1F:→ Feis:个人认为可以的话应该还是不要用 fgets 02/02 20:20
2F:→ Feis:此外 gary8520 的 code 应该跟原本的 code 也不同~ 02/02 20:23
3F:→ EdisonX:个人认为 fgets 是在 c-style 开发下的捷径,要正确的话当 02/02 20:48
4F:→ EdisonX:然它不会是首选,ifstream 比它正确,但相对的速度慢,不好用 02/02 20:49
5F:→ Feis:喔. 没有. 是我刚想错了 :P 02/02 20:57
6F:→ Feis:我想表达的是如果有极小的机率造成错误就别用 02/02 20:59
7F:→ Feis:不是 fgets 本身的问题. 而是原文的用法 02/02 21:01
8F:→ EdisonX:了解, 谢谢 :D 02/02 21:01
9F:推 gary8520:其实我那个code基本上是复制t大的,只是加了一些宣告之类 02/02 21:12
10F:→ gary8520:忘了谢谢E大大 02/02 21:14
11F:→ EdisonX:嗯,所以可能你没了解 bufsize 的含意吧,导致 fgets 有问题 02/02 21:14
12F:推 gary8520:我自认为我应该知道fgets那个方法,只要bufsize够大,能 02/02 21:18
13F:→ gary8520:读完一整行,其实fgets应该就可以跑出正确的结果。 02/02 21:18
14F:→ EdisonX:嗯,那是我误会你了. 附一提, edisonx==tropical72 . 02/02 21:21
15F:→ Feis:@gary8520: 你的 code 我觉得一定有什麽神秘力量. 建议贴一下 02/02 21:25
16F:→ purincess:t大魂魄不散 (? 02/02 22:14
17F:→ EdisonX:嗯... 还剩一口气 Orz 02/02 22:18
18F:→ freaky:如果只给Windows用,CreateFile/ReadFile是你的好朋友。 02/02 22:43
19F:→ freaky:先GetFileSizeEx()再看你要怎麽分段或一次读完都可以。 02/02 22:45
20F:→ freaky:可以从32Kb buffer大小开始测读取速度。 02/02 22:51
21F:→ freaky:读大档案memory-mapped file更快。 02/02 22:52