У меня есть программа для обработки очень больших файлов. Теперь мне нужно показать индикатор выполнения, чтобы показать ход обработки. Программа работает на уровне слов, читая по одной строке за раз, разбивая ее на слова и обрабатывая слова по одному. Таким образом, пока программы работают, он знает количество обработанных слов. Если каким-то образом он заранее знает количество слов в файле, он может легко вычислить прогресс.
Проблема в том, что файлы, с которыми я имею дело, могут быть очень большими, и поэтому не рекомендуется обрабатывать файл дважды, один раз, чтобы получить общее количество слов, а затем запустить фактический код обработки.
Итак, я пытаюсь написать код, который может оценить количество слов в файле, прочитав небольшую его часть. Вот что я придумал (в 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), он возвращает почти вдвое больше реального числа слов.
Я понимаю, что эта проблема из-за кодировки. Так есть ли способ ее решить?
РЕШЕНИЕ
По предложению Фрэнка я определяю количество байтов первых 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 символов, потому что это дает лучшую оценку.