我有一个程序来处理非常大的文件。现在我需要显示一个进度条来显示处理的进度。该程序在单词级别上工作,一次读取一行,将其拆分为单词并逐个处理单词。因此,当程序运行时,它知道处理的单词的数量。如果它以某种方式预先知道文件的字数,它可以很容易地计算进度。
问题是,我正在处理的文件可能非常大,因此处理文件两次不是一个好主意,一次是为了获得总字数,然后是运行实际处理代码。
所以我试着编写一个代码,通过读取文件的一小部分来估算文件的字数。这就是我提出的(在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编码)的文件上运行它时,它几乎返回真实字数的两倍。
据我所知,这个问题是因为编码问题。那有什么方法可以解决它吗?
解
作为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 :(得分:11)
为什么不根据处理的字节而不是单词计数来设置进度条。你知道前面的大小,然后主要的难点是在处理它们时获得每个字的字节数或每行的字节数。
执行此操作的最简单方法是为您读入的每一行,使用getBytes
,提供写入文件的字符编码,然后获取其长度。这可能不是最有效的方法,但它将非常准确和简单。
或者,您可以一次读取固定数量的字节,然后自己维护缓冲区以处理部分单词和换行符。
答案 1 :(得分:2)
在UTF-8中,印地文文本平均每个字符大约两个字节。您似乎读取了1000个字符,并将计算应用于文件长度(以字节为单位)。因此,如果您事先知道该语言,则可以补偿char与字节的比率。
否则,您可以确定前100个字符的字节数来估算比率。我不太了解Clojure,但也许您可以在读取1000个字符后将文件中的当前位置确定为具有搜索功能的某些变体的字节数?
答案 2 :(得分:0)
你能不能用chars-read / bytes-read的比例来补偿平均字节数/字符数?
答案 3 :(得分:0)
您的进度条需要多准确?我猜测答案不是“对0.1%准确率至关重要的任务”。在这种情况下,只需检查文件的大小及其编码,并使用硬编码的AVG_BYTES_PER_WORD与进度条一起使用。