Thư mục D chứa hàng nghìn email ở định dạng .eml. Một số email là văn bản thuần túy, một số từ Outlook, một số có tiêu đề ASCII và nội dung HTML/MIME, v.v. Tồn tại một tệp từ điển F chứa danh sách các từ thú vị cần tìm trong các tệp trong thư mục D (tức là red\nblue\ngreen\n...). Có một số lượng lớn các thư mục con trong thư mục D, nhưng không có tệp nào khác ngoại trừ tệp .eml ở trên. Những từ xuất hiện thường xuyên nhất phải được liệt kê theo các thông số kỹ thuật sau:
- Đối với mỗi từ thú vị, cần cung cấp thông tin về số lần nó xuất hiện và nơi nó xuất hiện. Nếu nó xảy ra nhiều lần trong một tệp thì nó sẽ được báo cáo nhiều lần cho tệp đó. Báo cáo một lần xuất hiện có nghĩa là báo cáo một bộ số nguyên (L,P), trong đó L là số dòng bắt đầu ở đầu nguồn email và P là vị trí trong dòng nơi sự xuất hiện bắt đầu.
Điều này sẽ xây dựng một chỉ mục để tham chiếu các lần xuất hiện khác nhau và tóm tắt các từ thú vị xuất hiện thường xuyên nhất.
Đầu ra phải nằm trên một tệp đầu ra duy nhất, định dạng không được xác định nghiêm ngặt, miễn là bao gồm các thông tin trên: các từ thú vị, số lần xuất hiện của mỗi từ thú vị và vị trí xuất hiện -> file/line/start- chức vụ.
Đây không phải là bài tập về nhà mà là một bài phân tích văn bản thực tế mà tôi muốn thực hiện trên một tập dữ liệu khá lớn. Thử thách của tôi là chọn đúng công cụ để lọc hiệu quả. Cách tiếp cận lặp đi lặp lại (sản phẩm Descartes của các từ/email/v.v.) quá chậm, sẽ tốt hơn nếu kết hợp nhiều bộ lọc từ cho mỗi dòng của mỗi tệp.
Tôi đã thử xây dựng một biểu thức chính quy thay thế từ danh sách các từ thú vị w1|w2|w3|... , biên dịch và chạy nó trên mỗi dòng của mỗi email, nhưng nó vẫn chậm, đặc biệt là khi tôi kiểm tra nhiều lần xuất hiện trong một chèo thuyền khi tôi cần.
例子:
Email E có dòng chữ:
^ ...đợi đã... táo đỏ... việt quất xanh... cờ đỏ trắng và xanh. $\n
Regex báo cáo chính xác màu đỏ (2) và màu xanh (2) nhưng chậm khi sử dụng một từ điển thực sự rất lớn gồm các từ thú vị.
Một cách tiếp cận khác tôi đã thử là:
Sử dụng cơ sở dữ liệu Sqlite để kết xuất thẻ trong quá trình phân tích cú pháp, bao gồm thông tin (cột, vị trí) cho mỗi mục nhập và cuối cùng truy vấn đầu ra. Với bộ nhớ đệm thích hợp, việc chèn số lượng lớn sẽ giúp ích rất nhiều nhưng lại tăng thêm độ phức tạp.
Tôi chưa thử song song hóa dữ liệu vì tôi không chắc chắn mã thông báo/phân tích cú pháp có phải là điều nên làm ngay từ đầu hay không. Có lẽ một cây bảng chữ cái sẽ phù hợp hơn?
Tôi quan tâm đến các giải pháp sau đây, theo thứ tự ưu tiên:
- Các công cụ Bash/GNU CLI (đặc biệt là các công cụ có thể song song hóa thông qua GNU "song song", chỉ dành cho thực thi CLI)
- Python (xử lý ngôn ngữ tự nhiên?)
- C/C++
Không có Perl, tiếc là tôi không hiểu.
Tôi giả sử bạn có thể tạo/tìm trình chuyển đổi eml sang văn bản. Chà, điều này rất gần với những gì bạn muốn:
tìm -type f | song song --tag 'eml-to-text {} |
Định dạng của đầu ra không phải là 100% những gì bạn muốn:
Tên file \t số dòng: số byte (bắt đầu từ file): word
Nếu bạn có nhiều từ thú vị,grep
'-f' trong sẽ khởi động rất chậm, vì vậy nếu bạn có thể tạo phiên bản giải nén của maildir, bạn có thể khởi động nó song song grep
Ít lần hơn:
tìm . -type f | song song 'eml-to-text {} >/tmp/unpacked/{#}'
tìm /tmp/unpacked -type f | song song -X grep -H -o -n -b -f /tmp/list_of_interesting_words
由于 grep -f
Độ phức tạp về thời gian kém hơn tuyến tính, bạn có thể muốn chia /tmp/list_of_interesting_words thành các khối nhỏ hơn:
cat /tmp/list_of_interesting_words | song song --pipe --block 10k --files > /tmp/blocks_of_words
Sau đó xử lý các khối và tệp song song:
tìm /tmp/unpacked -type f | song song -j1 -I ,, song song --arg-file-sep // -X grep -H -o -n -b -f ,, {} // - :::: /tmp/blocks_of_words
Định dạng của đầu ra này như sau:
Tên file: số dòng: số byte (bắt đầu từ file): word
Sắp xếp kết quả theo từ
Thay vì nhóm ống tên tệp:
... | sắp xếp -k4 -t: > index.by.word
Tính tần số:
... | sắp xếp -k4 -t: | tee index.by.word |
Tin vui là tốc độ này sẽ khá nhanh, tôi nghi ngờ bạn sẽ có thể đạt được tốc độ tương tự khi sử dụng Python.
biên tập:
grep -F khi khởi động nhanh hơn nhiều, bạn sẽ muốn -w cho grep (vì vậy từ "gram" không khớp với "sơ đồ"); điều này cũng sẽ tránh các tệp tạm thời và có thể khá nhanh:
tìm . -type f | song song --tag 'eml-to-text {} | grep -F -w -o -n -b -f /tmp/list_of_interesting_words' | từ | awk 'FS="{print $3}' |
Tôi là một lập trình viên xuất sắc, rất giỏi!