2010-08-18 39 views
5

Tôi có một chương trình xử lý các tệp rất lớn. Bây giờ tôi cần hiển thị thanh tiến trình để hiển thị tiến trình xử lý. Chương trình hoạt động trên một cấp độ từ, đọc từng dòng một, tách nó thành các từ và xử lý từng từ một. Vì vậy, trong khi các chương trình chạy, nó biết số lượng các từ được xử lý. Nếu bằng cách nào đó nó biết số lượng từ của tập tin trước, nó có thể dễ dàng tính toán tiến độ.Ước tính số lượng từ của một tệp mà không cần đọc toàn bộ tệp

Vấn đề là, các tệp tôi đang xử lý có thể rất lớn và do đó không nên xử lý tệp hai lần, một lần để nhận tổng số từ và tiếp theo để chạy mã xử lý thực.

Vì vậy, tôi đang cố gắng viết mã có thể ước tính số lượng từ của một tệp bằng cách đọc một phần nhỏ của tệp. Đây là những gì tôi đã đưa ra (trong 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))))) 

Mã này đọc 1000 ký tự đầu tiên từ tập tin, tạo một String từ nó, tokenizes nó để có được chữ, đếm từ và sau đó ước tính số lượng từ của tệp bằng cách nhân nó với chiều dài của tệp và chia nó theo 1000.

Khi tôi chạy mã này trên một tệp có văn bản tiếng Anh, tôi nhận được số từ gần đúng. Nhưng, khi tôi chạy nó trên một tập tin với văn bản tiếng Hin-ddi (được mã hóa bằng UTF-8), nó trở lại gần gấp đôi số lượng từ thực.

Tôi hiểu rằng vấn đề này là do mã hóa. Vì vậy, có cách nào để giải quyết nó?

SOLUTION

Như suggested by Frank, tôi xác định số byte của 10000 ký tự đầu tiên và sử dụng nó để ước tính số lượng từ của tập tin.

(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)))))) 

Lưu ý rằng điều này giả định mã hóa UTF-8. Ngoài ra, tôi quyết định đọc 10000 ký tự đầu tiên vì nó cho một ước tính tốt hơn.

+0

Tôi đoán bạn đang thông báo bằng cách sử dụng dấu cách (tôi không quen thuộc với glojure), đây là một lỗi khá phổ biến. Không phải mọi ngôn ngữ đều sử dụng khoảng trắng (hoặc bất kỳ thứ gì khác) cho các ranh giới từ. – whiskeysierra

+0

@ WilliSchönborn: Tôi không thông báo bằng cách sử dụng dấu cách. Tôi đang sử dụng regex thuộc tính Unicode '[\\ p {Z} \\ p {C} \\ p {P}] +'. –

+0

Ah, ok. Cú pháp lạ. – whiskeysierra

Trả lời

2

Trong UTF-8, văn bản tiếng Hindi trung bình khoảng hai byte mỗi char. Dường như bạn đọc 1000 ký tự và áp dụng phép tính cho độ dài tệp theo byte. Vì vậy, nếu bạn xảy ra để biết ngôn ngữ trước, bạn có thể bù đắp cho tỷ lệ char để byte.

Nếu không, bạn có thể xác định số byte của 100 ký tự đầu tiên để ước tính tỷ lệ. Tôi không biết Clojure rất tốt, nhưng có lẽ bạn có thể xác định vị trí hiện tại trong tệp dưới dạng số byte với một số biến thể của hàm tìm kiếm sau khi đọc 1000 ký tự?

0

Bạn không thể bù cho số byte/char trung bình với tỷ lệ đọc ký tự/đọc byte?

11

Tại sao không chỉ tạo thanh tiến trình dựa trên các byte được xử lý thay vì đếm từ. Bạn biết kích thước trả trước, và sau đó khó khăn lớn là chỉ nhận được các byte cho mỗi từ hoặc byte trên mỗi dòng khi bạn xử lý chúng.

Cách dễ nhất để làm điều này là cho mỗi dòng bạn đọc, sử dụng getBytes, cung cấp mã hóa ký tự mà tệp đã được viết và sau đó nhận được độ dài đó. Đây có thể không phải là cách hiệu quả nhất để làm điều đó, nhưng nó sẽ rất chính xác và đơn giản để làm.

Hoặc, bạn có thể đọc theo số byte cố định tại một thời điểm, và sau đó duy trì bộ đệm để xử lý một phần từ và ngắt dòng.

0

Thanh tiến trình của bạn cần phải chính xác như thế nào? Tôi đoán câu trả lời không phải là "nhiệm vụ quan trọng với độ chính xác 0,1%". Trong trường hợp đó, chỉ cần kiểm tra kích thước của tệp và mã hóa và có mã hóa AVG_BYTES_PER_WORD được mã hóa cứng để sử dụng với thanh tiến trình của bạn.

Các vấn đề liên quan