2010-08-18 10 views
5

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.

+0

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

+0

@ WilliSchönborn: No estoy tokenizando usando espacios. Estoy usando la expresión de propiedad Unicode '[\\ p {Z} \\ p {C} \\ p {P}] +'. –

+0

Ah, está bien. Extraña sintaxis – whiskeysierra

Respuesta

2

En UTF-8, el texto en hindi promedia aproximadamente dos bytes por char. Parece que lee 1000 caracteres y aplica el cálculo a la longitud del archivo en bytes. Por lo tanto, si conoce el idioma de antemano, puede compensar la proporción de caracteres por bytes.

De lo contrario, podría determinar el número de bytes de los primeros 100 caracteres para estimar la relación. No conozco Clojure muy bien, pero tal vez pueda determinar la posición actual en el archivo como un conteo de bytes con alguna variante de una función de búsqueda después de haber leído 1000 caracteres.

0

¿No puede compensar el número promedio de bytes/char con la proporción de chars-read/bytes-read?

11

¿Por qué no acaba de hacer la barra de progreso en función de los bytes procesados ​​en lugar de un recuento de palabras? Conoces el tamaño por adelantado, y luego la mayor dificultad es obtener los bytes por palabra o bytes por línea a medida que los procesas.

La manera más fácil de hacer esto es por cada línea que lee, use getBytes, proporcionando la codificación de caracteres en la que se escribió el archivo, y luego obtenga la longitud de eso. Esta puede no ser la forma más eficiente de hacerlo, pero será muy precisa y simple de hacer.

Como alternativa, podría leer en un número fijo de bytes a la vez, y luego mantener un búfer para manejar palabras parciales y saltos de línea.

0

¿Qué tan precisa debe ser tu barra de progreso? Supongo que la respuesta no es "misión crítica para el 0,1% de precisión". En ese caso, simplemente verifique el tamaño del archivo y su codificación y tenga codificado AVG_BYTES_PER_WORD para usar con su barra de progreso.

Cuestiones relacionadas