2010-08-18 3 views
5

매우 큰 파일을 처리하는 프로그램이 있습니다. 이제 처리 진행 상황을 보여주는 진행률 막대를 표시해야합니다. 이 프로그램은 단어 단위로 작동하고 한 번에 한 줄씩 읽으며 단어 단위로 나누어 하나씩 처리합니다. 따라서 프로그램이 실행되는 동안 처리 된 단어의 수를 알 수 있습니다. 여하튼 파일의 단어 수를 미리 알고 있다면 쉽게 진행 상황을 계산할 수 있습니다.전체 파일을 읽지 않고 파일의 단어 수를 계산하십시오.

문제는 내가 다루는 파일이 매우 클 수 있으므로 파일을 두 번 처리해야합니다. 한번은 총 단어 수를 얻고 그 다음은 실제 처리 코드를 실행하는 것이 좋습니다.

그래서 나는 파일의 일부분을 읽음으로써 파일의 단어 수를 추정 할 수있는 코드를 작성하려고합니다.

(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 개 문자를 읽고 그것에서 문자열을 만들고, 단어를 얻을 수를 토큰 화는 단어를 계산 한 후 추정 : 이것은 내가 (Clojure의에서) 함께 온 것입니다 파일의 길이를 파일의 길이에 곱하여 1000으로 나눕니다.

이 코드를 영어 텍스트 파일에서 실행할 때 단어 수는 거의 정확합니다. 그러나 힌디어 텍스트 (UTF-8로 인코딩 됨)로 파일을 실행하면 실제 단어 수가 거의 두 배가됩니다.

이 문제는 인코딩으로 인한 것으로 알고 있습니다. 그래서 그것을 해결할 방법이 있습니까?

솔루션

suggested by 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)))))) 

여기서는 UTF-8 인코딩을 가정합니다. 또한 더 나은 견적을 제공하기 때문에 처음 10000 문자를 읽도록 결정했습니다.

+0

공백을 사용하여 토큰 화하는 것 같습니다. (나는 글로 구어에 익숙하지 않습니다.) 이것은 꽤 일반적인 실수입니다. 모든 언어가 단어 경계에 공백 (또는 다른 것)을 사용하는 것은 아닙니다. – whiskeysierra

+0

@ WilliSchönborn : 나는 공간을 사용하여 토큰을 만들고 있지 않습니다. 유니 코드 속성 regex'[\\ p {Z} \\ p {C} \\ p {P}] +'를 사용하고 있습니다. –

+0

아, 좋아. 이상한 구문. – whiskeysierra

답변

2

UTF-8에서 힌디어 텍스트의 평균은 char 당 약 2 바이트입니다. 당신은 1000자를 읽고, 바이트 단위의 파일 길이에 계산을 적용하는 것처럼 보입니다. 따라서 사전에 언어를 알고 있다면 char 대 byte 비율을 보상 할 수 있습니다.

그렇지 않으면 비율을 추정하기 위해 처음 100 자의 바이트 수를 결정할 수 있습니다. 나는 Clojure를 잘 모른다. 그러나 파일의 1000 바이트를 읽은 후 seek 함수의 일부 변형을 사용하여 파일의 현재 위치를 바이트 수로 결정할 수 있을까?

0

chars-read/bytes-read의 비율로 평균 바이트/문자 수를 보완 할 수 없습니까?

11

단어 카운트 대신 처리 된 바이트를 기반으로 진행률 표시 줄을 만드는 것만으로는 안됩니다. 당신은 크기를 미리 알고 있고, 그 다음 큰 어려움은 당신이 처리 할 때 한 줄당 바이트 나 한 줄당 바이트를 얻는 것입니다.

가장 쉬운 방법은 파일을 작성한 문자 인코딩을 제공하고 getBytes을 사용하여 읽는 각 줄에 대한 것입니다. 이것은 가장 효율적인 방법은 아니지만 매우 정확하고 간단합니다.

또는 한 번에 고정 된 바이트 수를 읽은 다음 부분 단어 및 줄 바꿈을 처리 할 버퍼를 직접 유지할 수 있습니다.

0

진행률 막대의 정확도는 어느 정도입니까? 대답은 "0.1 % 정확도에 중요한 임무"가 아니라고 생각합니다. 이 경우 파일의 크기를 확인하고 인코딩하고 진행 막대에 사용할 AVG_BYTES_PER_WORD를 하드 코딩해야합니다.