2010-08-18 95 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与您的进度栏一起使用。