Оценка количества слов в файле без чтения полного файл

У меня есть программа для обработки очень больших файлов. Теперь мне нужно показать индикатор выполнения, чтобы показать ход обработки. Программа работает на уровне слов, читая по одной строке за раз, разбивая ее на слова и обрабатывая слова по одному. Таким образом, пока программы работают, он знает количество обработанных слов. Если каким-то образом он заранее знает количество слов в файле, он может легко рассчитать прогресс.

Проблема в том, что файлы, с которыми я имею дело, могут быть очень большими, и, следовательно, не очень хорошая идея обрабатывать файл дважды, один раз, чтобы получить общее количество слов, а затем запустить фактический код обработки.

Поэтому я пытаюсь написать код, который может оценить количество слов в файле, прочитав небольшую его часть. Вот что я придумал (в 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 символов, потому что это дает лучшую оценку.

5
задан Community 23 May 2017 в 12:11
поделиться