Tengo un programa para procesar archivos muy grandes. Ahora necesito mostrar una barra de progreso para mostrar el progreso del procesamiento. El programa funciona en un nivel de palabra, lee una línea a la vez, dividiéndola en palabras y procesando las palabras una a una. Entonces, mientras se ejecutan los programas, conoce el recuento de las palabras procesadas. Si de alguna manera conoce el conteo de palabras del archivo de antemano, puede calcular fácilmente el progreso.Estimación del conteo de palabras de un archivo sin leer el archivo completo
El problema es que los archivos que estoy tratando pueden ser muy grandes y no es una buena idea procesar el archivo dos veces, una para obtener el total de palabras y el siguiente para ejecutar el código de procesamiento real.
Así que estoy tratando de escribir un código que puede estimar el número de palabras de un archivo leyendo una pequeña porción de él. Esto es lo que he llegado con (en 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)))))
Este código lee los primeros 1000 caracteres del archivo, crea una cadena de ella, tokenizes para obtener palabras, cuenta las palabras y luego se estima la recuento de palabras del archivo multiplicándolo por la longitud del archivo y dividiéndolo por 1000.
Cuando ejecuto este código en un archivo con texto en inglés, obtengo un recuento de palabras casi correcto. Pero cuando ejecuto esto en un archivo con texto Hindi (codificado en UTF-8), devuelve casi el doble del recuento de palabras reales.
Entiendo que este problema se debe a la codificación. Entonces, ¿hay alguna forma de resolverlo?
SOLUCIÓN
Como suggested by Frank, determino el número de bytes de los primeros 10000 caracteres y lo uso para estimar el número de palabras del archivo.
(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))))))
Tenga en cuenta que esto supone la codificación UTF-8. Además, decidí leer primero 10000 caracteres porque da una mejor estimación.
Supongo que está tokenizando usando espacios (no estoy familiarizado con glojure), que es un error bastante común. No todos los idiomas usan espacios en blanco (o cualquier otra cosa) para los límites de las palabras. – whiskeysierra
@ WilliSchönborn: No estoy tokenizando usando espacios. Estoy usando la expresión de propiedad Unicode '[\\ p {Z} \\ p {C} \\ p {P}] +'. –
Ah, está bien. Extraña sintaxis – whiskeysierra