2011-03-03 126 views
9

我需要讀取一行文本(以換行符結尾)而不對長度進行假設。所以,我現在面對的可能:C fgets與讀取行的fgetc

  • 使用fgets每一次檢查,如果最後一個字符是一個換行符,不斷追加到緩衝區
  • 使用fgetc偶爾realloc緩衝
閱讀每個字符

直覺告訴我fgetc變種可能會變慢,但是我再次看不到fgets沒有檢查每個字符(我的直覺並不總是那麼好)。線條非常大,因此性能很重要。

我想知道每種方法的優缺點。先謝謝你。

回答

1

我建議使用fgets()加上動態內存分配 - 或者您可以調查getline()的接口,該接口符合POSIX 2008標準,並可在更新的Linux機器上使用。這爲你的內存分配的東西。您需要密切關注緩衝區的長度和地址 - 所以您甚至可以創建一個結構來處理信息。

儘管fgetc()也可以工作,但它略微偏離 - 但只是略微如此。在封面下方,它使用與fgets()相同的機制。內部可能能夠利用更快的操作 - 類似於strchr() - 當您直接撥打fgetc()時不可用。

+0

使用'fgets'實現'getline'函數時的一個限制是,不可能同時處理不以換行符結尾的空字節**和**文件。如果'fgets'遇到EOF條件並且沒有換行符而返回,則只能假設該字符串在第一個空字節結束。 (在其他情況下,您可以執行'strchr(buf,'\ n')'來查找讀取停止的位置 - 或者如果沒有''\ n'',您需要'realloc'。) – mk12 2012-08-21 22:44:09

+0

如果文件包含空字節,它不是一個文本文件。 (它可能是一個寬字符文件,但是你需要使用寬字符I/O函數來讀取它。)而'fgets()'並不是用來處理包含空字節的文件 - 正是因爲它沒有給出可靠地指示它讀取的字節數。如果你的數據文件包含空字節,你應該(可能)不用'fgets()'來讀取它。 – 2012-08-21 22:53:38

+0

http://linux.die.net/man/3/getline(返回值部分)似乎表明它可能是一個有用的東西。這就是我想到的地方,但我想我也同意你的看法。現在我想到了,也許這只是在那裏提到的,因爲當使用除'\ n''之外的分隔符時它可能是有用的。 – mk12 2012-08-21 22:58:16

0

如果你可以設置一個最大的行長,即使是一個大的,然後一個fgets會做的伎倆。如果不是,多個fgets調用仍然會比多個fgetc調用更快,因爲後者的開銷會更大。

更好的答案是,除非必須,否則不值得擔心性能差異。如果fgetc足夠快,那有什麼關係?

+0

另請注意,'getc'通常是作爲一個宏來實現的,因此它比'fgetc'更快,只要你小心(參數不能是表達式),就應該使用它。 – mk12 2012-08-21 22:50:38

2

您的環境是否提供getline(3)功能?如果是這樣,我會說去那。

我看到的一大優點是它自己分配緩衝區(如果需要),並且如果緩衝區太小,您將傳入緩衝區realloc()。 (所以這意味着你需要傳遞從malloc()得到的東西)。

這可以擺脫fgets/fgetc的一些痛苦,並且您可以希望編寫實現它的C庫負責使其高效。

紅利:Linux上的手冊頁有一個很好的例子,說明如何高效地使用它。

+0

不幸的是(我很抱歉,我沒有提到這個問題)我需要使用標準的東西:-(getline函數肯定聽起來很有吸引力 – nc3b 2011-03-03 21:08:36

+1

嗯,它是標準的(對於標準的某些定義)見[The Open Group Base Specifications Specifications Issue 7](http://pubs.opengroup.org/onlinepubs/9699919799/),又名「IEEE Std 1003.1™-2008」又名「POSIX C 2008」,但標準!=很普遍,不幸的是,我感覺你的痛苦getline很性感:-) – Mat 2011-03-03 21:15:33

+0

'getline()'功能很好; 'getline()'這個名字是對用戶命名空間的一種巨大侵入,它使用了廣泛使用的函數名稱之一(例如參見K&R 1和2),其中包含各種不同的接口。使用這個名字是一個令人震驚的決定;提供功能是一個非常好的決定。唯一令人驚訝的是沒有處理CRLF行結束的能力;相關的'getdelim()'函數可以處理CR或LF或NUL行尾,但不能處理CRLF行尾。 – 2011-03-04 00:20:16

0

我會分配一個大緩衝區,然後使用fgets,檢查,重新分配和重複,如果你沒有閱讀到行的末尾。每次你讀(通過fgetc或fgets)你正在進行一次系統調用,這需要花費時間,所以你想最小化發生的次數,所以調用fgets的次數更少,並且在內存中迭代的速度更快。

如果您正在讀取文件,mmap() ing是另一個選項。

+0

我必須在系統調用部分中與您相矛盾:stdio庫會進行緩衝,所以我不認爲每個函數調用都會被轉換爲系統調用。我可能錯了 – nc3b 2011-03-03 21:13:29

+0

這是真的,但用fgets他會有更好的粒度控制。如果他對線的平均時間有一些瞭解,他可以優化緩衝區長度,而不是fgetc,這會緩衝,但對理想緩衝區長度完全不知道。 – 2011-03-03 21:18:25

2

如果性能對您來說很重要,您通常需要撥打getc而不是fgetc。該標準試圖使getc更容易實現爲宏,以避免函數調用開銷。

過去,處理的主要問題可能是分配緩衝區的策略。大多數人使用固定的增量(例如,當/如果我們用完空間,分配另外的128個字節)。我建議使用常數因子,所以如果空間不足,請分配一個緩衝區,例如,比前一個大小的1.5倍。

特別是當getc被實現爲宏時,getcfgets之間的差異通常非常小,因此您最好專注於其他問題。

+0

+1謝謝,這有助於:-) – nc3b 2011-03-03 21:16:31