我有一個程序來處理非常大的文件。現在我需要顯示一個進度條來顯示處理進度。該程序在單詞級別上工作,一次只讀一行,將其分成單詞並逐個處理單詞。所以當程序運行時,它知道處理的字數。如果事先知道文件的字數,它可以很容易地計算進度。估計文件的字數而不讀取完整文件
問題是,我正在處理的文件可能非常大,因此處理該文件兩次並不是一個好主意,一次獲得總字數並接着運行實際處理代碼。
所以我想寫一個代碼,它可以通過讀取它的一小部分來估計文件的字數。這是我想出了(Clojure中):
(defn estimated-word-count [file]
(let [^java.io.File file (as-file file)
^java.io.Reader rdr (reader file)
buffer (char-array 1000)
chars-read (.read rdr buffer 0 1000)]
(.close rdr)
(if (= chars-read -1)
0
(* 0.001 (.length file)
(-> (String. buffer 0 chars-read) tokenize-line count)))))
此代碼從文件中讀取前1000個字符,從它創建一個字符串,標記化它得到的話,計算的話,然後估計將文件的字數乘以文件長度併除以1000.
當我在帶有英文文本的文件上運行此代碼時,我得到的字數幾乎是正確的。但是,當我用含有北印度文字的文件(用UTF-8編碼)運行此文件時,它幾乎會返回真實文字數的兩倍。
我知道這個問題是因爲編碼。那麼有什麼方法可以解決它?
SOLUTION
由於suggested by Frank,我確定第10000個字符的字節數和 用它來估計文件的字數。
(defn chars-per-byte [^String s]
(/ (count s) ^Integer (count (.getBytes s "UTF-8"))))
(defn estimate-file-word-count [file]
(let [file (as-file file)
rdr (reader file)
buffer (char-array 10000)
chars-read (.read rdr buffer 0 10000)]
(.close rdr)
(if (= chars-read -1)
0
(let [s (String. buffer 0 chars-read)]
(* (/ 1.0 chars-read) (.length file) (chars-per-byte s)
(-> s tokenize-line count))))))
請注意,這是假設UTF-8編碼。另外,我決定閱讀前10000個字符,因爲它提供了一個更好的估計。
我想你是使用空格(我不熟悉glojure)的標記,這是一個相當常見的錯誤。並非所有語言都使用空格(或其他)來限制單詞邊界。 – whiskeysierra 2010-08-18 23:24:04
@WilliSchönborn:我不使用空格來標記化。我正在使用Unicode屬性正則表達式'[\\ p {Z} \\ p {C} \\ p {P}] +'。 – 2010-08-19 06:34:52
啊,好的。奇怪的語法。 – whiskeysierra 2010-08-19 13:21:42