2010-08-18 105 views
5

我有一個程序來處理非常大的文件。現在我需要顯示一個進度條來顯示處理進度。該程序在單詞級別上工作,一次只讀一行,將其分成單詞並逐個處理單詞。所以當程序運行時,它知道處理的字數。如果事先知道文件的字數,它可以很容易地計算進度。估計文件的字數而不讀取完整文件

問題是,我正在處理的文件可能非常大,因此處理該文件兩次並不是一個好主意,一次獲得總字數並接着運行實際處理代碼。

所以我想寫一個代碼,它可以通過讀取它的一小部分來估計文件的字數。這是我想出了(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個字符,因爲它提供了一個更好的估計。

+0

我想你是使用空格(我不熟悉glojure)的標記,這是一個相當常見的錯誤。並非所有語言都使用空格(或其他)來限制單詞邊界。 – whiskeysierra 2010-08-18 23:24:04

+0

@WilliSchönborn:我不使用空格來標記化。我正在使用Unicode屬性正則表達式'[\\ p {Z} \\ p {C} \\ p {P}] +'。 – 2010-08-19 06:34:52

+0

啊,好的。奇怪的語法。 – whiskeysierra 2010-08-19 13:21:42

回答

2

在UTF-8中,印地語文本平均每個字符大約兩個字節。您似乎讀取1000個字符,並將計算應用於文件長度(以字節爲單位)。因此,如果您事先知道該語言,則可以補償字符與字節的比率。

否則,您可以確定前100個字符的字節數來估計比率。我不太瞭解Clojure,但也許你可以在讀取1000個字符之後將文件中的當前位置確定爲字節計數,並帶有某種查找函數的變體?

0

難道你不能用char/read/bytes-read的比率來補償字節/字符的平均數嗎?

11

爲什麼不根據處理的字節而不是字數統計進度條。您知道前面的大小,然後主要難點是在處理它們時獲取每個字的字節或每行的字節數。

最簡單的方法是讀取每行,使用getBytes,提供文件寫入的字符編碼,然後獲取長度。這可能不是最有效的方法,但它會非常準確和簡單。

或者,您可以一次讀入固定數量的字節,然後自行維護一個緩衝區以處理部分字詞和換行符。

0

你的進度條需要多準確?我猜測答案不是「對0.1%準確的任務至關重要」。在這種情況下,只需檢查文件的大小及其編碼,並使用硬編碼的AVG_BYTES_PER_WORD與您的進度欄一起使用。