在不读取完整文件的情况下估计文件的字数

Abh*_*kar 5 java text-processing clojure large-files

我有一个程序来处理非常大的文件.现在我需要显示一个进度条来显示处理的进度.该程序在单词级别上工作,一次读取一行,将其拆分为单词并逐个处理单词.因此,当程序运行时,它知道处理的单词的数量.如果它以某种方式预先知道文件的字数,它可以很容易地计算进度.

问题是,我正在处理的文件可能非常大,因此处理文件两次不是一个好主意,一次得到总字数,然后是运行实际处理代码.

所以我试着编写一个代码,通过读取文件的一小部分来估算文件的字数.这就是我提出的(在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)))))
Run Code Online (Sandbox Code Playgroud)

此代码从文件中读取前1000个字符,从中创建一个String,对其进行标记以获取单词,对单词进行计数,然后通过将文件的长度乘以文件的长度并将其除以1000来估计文件的单词计数.

当我在带有英文文本的文件上运行此代码时,我得到几乎正确的字数.但是,当我在一个带有印地文文本(用UTF-8编码)的文件上运行它时,它几乎返回真实字数的两倍.

我知道这个问题是因为编码.那么有什么方法可以解决它吗?

正如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))))))
Run Code Online (Sandbox Code Playgroud)

请注意,这假设为UTF-8编码.此外,我决定先阅读10000个字符,因为它可以提供更好的估算.

Rus*_*ett 11

为什么不根据处理的字节而不是字数来设置进度条.您知道前面的大小,然后主要的难点是在处理它们时获取每个字的字节数或每行的字节数.

最简单的方法是为您读入的每一行使用getBytes,提供写入文件的字符编码,然后获取其长度.这可能不是最有效的方法,但它将非常准确和简单.

或者,您可以一次读取固定数量的字节,然后自己维护缓冲区以处理部分单词和换行符.


Fra*_*ank 2

在 UTF-8 中,印地语文本平均每个字符约为两个字节。您似乎读取了 1000 个字符,并将计算结果应用于文件长度(以字节为单位)。因此,如果您事先了解该语言,则可以补偿字符与字节的比率。

否则,您可以确定前 100 个字符的字节数来估计比率。我不太了解 Clojure,但也许您可以在读取 1000 个字符后,使用搜索函数的某些变体将文件中的当前位置确定为字节数?