我有一个程序来处理非常大的文件。现在我需要显示一个进度条来显示处理进度。该程序在单词级别上工作,一次只读一行,将其分成单词并逐个处理单词。所以当程序运行时,它知道处理的字数。如果事先知道文件的字数,它可以很容易地计算进度。估计文件的字数而不读取完整文件
问题是,我正在处理的文件可能非常大,因此处理该文件两次并不是一个好主意,一次获得总字数并接着运行实际处理代码。
所以我想写一个代码,它可以通过读取它的一小部分来估计文件的字数。这是我想出了(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