• Vui lòng đọc nội qui diễn đàn để tránh bị xóa bài viết
  • Tìm kiếm trước khi đặt câu hỏi

Cấu trúc tập tin dữ liệu từ điển của Babylon

Bạn muốn open source dự án mình đang phát triển để mọi người cùng theo dõi tiến độ, góp ý kiến, hổ trợ và tiếp sức thêm? Hãy đăng ký dự án mã nguồn mở của mình tại đây

Điều hành viên: Điều hành

Hình đại diện của người dùng
dazzlingvit
Guru
Guru
Bài viết: 959
Ngày tham gia: T.Ba 18/01/2011 10:21 am
Đến từ: Sinh ra từ hư vô, sống trong thế giới ảo...
Has thanked: 7 time
Been thanked: 112 time
Liên hệ:

Cấu trúc tập tin dữ liệu từ điển của Babylon

Gửi bàigửi bởi dazzlingvit » T.Hai 22/08/2011 5:40 pm

Cấu trúc tập tin dữ liệu từ điển của Babylon

The structure of Babylon Glossary file (.BGL)


Babylon là một phần mềm từ điển khá nổi tiếng nhờ kho dữ liệu rất lớn đủ các loại từ điển lớn nhỏ và khả năng bắt chữ (Click&See) đáng kinh ngạc. Kho từ điển của Babylon có cả những từ điển nổi tiếng của Oxford, Longman... hay các bộ từ điển của Việt Nam như Lạc việt, Prodic...
Tuy nhiên Babylon là phần mềm thương mại, và ngay cả những bộ từ điển lớn của Babylon cũng là từ điển mất phí. Về phần phát âm, Babylon chỉ sử dụng phát âm kiểu TTS (Text-to-speech), chính vì thế giọng đọc dù sao cũng không thể chuẩn xác. Hơn nữa, mỗi khi nạp một bộ từ điển (có dạng .BGL) nào vào Babylon, bộ từ điển này sẽ được "biên dịch" thành một dạng "dễ đọc" (đuôi .BDC), kích thước của tập tin này là khá lớn (sở dĩ kích thước của tập tin .BGL nhỏ hơn là do nó đã được nén, sẽ nói ở phần dưới), chiếm dụng khá nhiều dung lượng đĩa cứng, và quá trình nạp cũng mất nhiều thời gian.
Do sử dụng một máy tính có cấu hình không cao lắm, RAM cũng bé mà ổ cứng cũng chật nên mình đã nảy ra một ý tưởng là phải làm ra một phần mềm từ điển nào nhỏ nhẹ như kiểu Stardict nhưng phải đọc được nhiều định dạng hơn, đặc biệt là khai thác được kho dữ liệu lớn của Babylon. Sau một thời gian tham khảo một số bài viết trên các diễn đàn của nước ngoài và tự nghiên cứu, phân tích, cuối cùng mình cũng đã "hiểu" được những thông tin quan trọng nhất chứa trong tập tin .BGL của Babylon.
Trong bài viết này, mình muốn chia sẻ với các bạn những điều mình đã lấy ra được từ định dạng này, rất mong bạn nào có tâm huyết và hứng thú với ý tưởng này thì cùng tham gia với mình để tạo ra một chương trình từ điển nhỏ, nhanh mà lại mạnh. Dự định của mình sau bài viết này là mình sẽ tìm cách "lấy" được bộ dữ liệu phát âm của OALD8 (Oxford Advanced Learner's Dictionary 8th Edition) trên trang chủ của bộ OALD này. Tất nhiên đây là một công việc mất thời gian nhưng cũng không phức tạp lắm, nếu bạn nào có hứng thú thì có thể tham gia tìm hiểu về vấn đề này.

Những thông tin dưới đây là không đầy đủ (vì trình độ có hạn, số mẫu phân tích chưa đủ đa dạng, và những từ điển "khủng" thường được tạo ra bằng công cụ đặc biệt của Babylon nên chứa đựng nhiều thông tin "ẩn"). Rất mong các bạn đóng góp thêm để bản phân tích này ngày càng hoàn thiện.

1. Phần mở đầu (header):
+ Mở đầu tập tin là 4 byte chứa chữ ký (signature) của tập tin. Với định dạng .BGL, phần chữ ký sẽ là 0x12340001 hoặc 0x12340002 (0x ở đây cho biết phần sau là số theo hệ cơ số 16. Trong toàn bộ bài viết này mình đều sử dụng cách viết số ở hệ cơ số 16 theo kiểu của C/C++).
+ 2 byte tiếp theo chứa vị trí của phần mở đầu định dạng nén Gzip. Toàn bộ dữ liệu từ điển của định dạng .BGL được nén bằng thuật toán Gzip và lưu ở vị trí này. Byte mở đầu của định dạng nén này là 0x1F.
+ Các byte tiếp theo hiện chưa rõ.

Hình ảnh


2. Phần dữ liệu: phần dữ liệu được bắt đầu từ vị trí của phần mở đầu định dạng nén Gzip (0x1F). Vị trí này được lưu trữ ở byte thứ 0x04 và 0x05 (đã nói ở phía trên).
Phần dữ liệu (sau khi đã giải nén) được tổ chức dưới dạng một danh sách các bản ghi (Record) đặt liên tục từ đầu đến cuối (khi phần dữ liệu được giải nén, ngay byte đầu tiên đã là thông tin của bản ghi đầu tiên, không có bất kỳ dữ liệu nào ở phía trước). Tuy nhiên, các bản ghi này có kích thước không cố định (phụ thuộc vào thông tin chứa trong phần mở đầu của mỗi bản ghi), chính vì thế việc truy cập các bản ghi sẽ phải diễn ra một cách tuần tự từ bản ghi 1 đến bản ghi cuối cùng. Hơn nữa, việc đọc trực tiếp định dạng Gzip không hỗ trợ chức năng nhảy (seek) như với tập tin bình thường (zlib cũng hỗ trợ việc nhảy, nhưng đó chỉ là giả lập (simulation) nhảy bằng cách đọc liên tục từng byte một để đến được vị trí cần nhảy). Đây là một điều đáng lưu ý trong quá trình khai thác định dạng này. Vì nếu muốn tránh việc phải "biên dịch" định dạng này sang một định dạng "dễ đọc" hơn và chấp nhận kích thước lớn, chúng ta sẽ phải tạo những thông tin tạm (cache) hợp lý.

Cấu trúc mỗi bản ghi trong phần dữ liệu:
+ Phần mở đầu: chứa thông tin về loại bản ghi và kích thước dữ liệu của bản ghi (dữ liệu của bản ghi được lưu trữ ngay sau phần mở đầu).
Byte đầu tiên của phần mở đầu bao gồm 2 phần:
- 4 bit thấp chứa loại bản ghi.
- 4 bit cao liên quan đến kích thước của dữ liệu. Nếu 4 bit này >= 4, ta trừ đi 4 thì sẽ được kích thước của dữ liệu. Nếu 4 bit này < 4, ta cộng thêm 1 sẽ được một số cho biết kích thước của dữ liệu sẽ lưu trong bao nhiêu byte tiếp theo. Ví dụ 4 bit này là 0x3, cộng 1 ta được 0x4, tức là 4 byte tiếp theo sẽ chứa kích thước dữ liệu (dĩ nhiên 4 byte thì sẽ là kiểu int).
Phần trên có thể thấy ngay cả kích thước phần mở đầu của mỗi bản ghi cũng không hề cố định.
+ Phần dữ liệu: chứa toàn bộ thông tin về bản ghi. Mỗi loại bản ghi sẽ có cấu trúc phần dữ liệu khác nhau, thậm chí ngay cả trong phần dữ liệu cũng chứa thông tin về loại bản ghi con... Sau đây chúng ta sẽ khảo sát từng loại bản ghi.

Hình ảnh
(Hình trên đánh dấu 3 bản ghi đầu tiên của phần dữ liệu để làm ví dụ. Bản ghi đầu tiên có byte đầu tiên là 0x60, tức là bản ghi loại 0, kích thước dữ liệu là 0x6 - 0x4 = 0x2. Bản ghi tiếp theo cũng tương tự. Bản ghi tiếp có kích thước dữ liệu là 0x8 - 0x4 = 0x4)


Cấu trúc phần dữ liệu của các loại bản ghi:
1. Các bản ghi chứa thông tin chung của từ điển: đây là một tập hợp các bản ghi thuộc loại bản ghi 0x3 chứa những thông tin riêng lẻ chung về từ điển như số lượng mục từ, tiêu đề, tác giả...
a) Bản ghi chứa số lượng mục từ (entry): đây có lẽ là bản ghi thông tin chung quan trọng nhất, bởi vì dựa vào bản ghi này ta sẽ biết được số lượng mục từ trong toàn bộ từ điển, giúp cho quá trình "xử lý" từ điển diễn ra trực quan hơn (vì chúng ta có thể tạo một cái ProgressBar chạy cho người dùng đỡ sốt ruột). Bản ghi này luôn được đặt ở trên tất cả các bản ghi chứa mục từ.
+ 2 byte đầu chứa mã loại bản ghi thông tin chung: 0x000C.
+ 4 byte tiếp theo chứa số lượng mục từ.
Cần lưu ý là số lượng mục từ trong từ điển và số lượng từ trong từ điển là khác nhau. Tức là cùng một mục từ của từ điển có thể có nhiều từ cùng "trỏ" tới đó. Cái này sẽ nói rõ hơn trong phần bản ghi chứa mục từ.

Hình ảnh
(Phần được khoanh đỏ là bản ghi chứa số lượng mục từ. Byte mở đầu 0xA3 cho thấy bản ghi này thuộc loại 0x3, kích thước dữ liệu là 0xA - 0x4 = 0x6. 2 byte đầu tiên của phần dữ liệu chứa mã loại bản ghi thông tin chung 0x000C, tức là đây là loại bản ghi chứa số lượng mục từ. Tiếp đó là 4 byte 0x00005BEC chính là số lượng mục từ của từ điển)


b) Bản ghi chứa văn bản: đây là những bản ghi chứa thông tin chung của từ điển dạng văn bản, như tên từ điển, tên tác giả, mô tả chung...
+ 2 byte đầu tiên chứa mã loại bản ghi thông tin chung.
+ Các byte tiếp theo cho đến kết thúc bản ghi chứa thông tin dạng văn bản của bản ghi.

Hình ảnh
(Hình trên cho thấy bản ghi thuộc loại 0x3, kích thước được lưu trong 0x0 + 0x1 = 0x1 byte tiếp theo, chính là byte có giá trị 0x5A. Tiếp theo là 2 byte chứa giá trị 0x0009 chính là mã loại bản ghi thông tin chung. Sau đó là toàn bộ nội dung của bản ghi).

+ Các loại bản ghi chứa văn bản và mã loại bản ghi thông tin chung tương ứng:
- Tiêu đề (Title): 0x0001.
- Tác giả (Author): 0x0002.
- Địa chỉ thư điện tử (E-mail): 0x0003.
- Bản quyền (Copyright): 0x0004.
- Thông tin mô tả (Description): 0x0009.


c) Bản ghi chứa thông tin về bảng mã (character set):
+ 2 byte đầu chứa mã loại bản ghi thông tin chung: 0x001A cho bảng mã nguồn (Source charset), 0x001B cho bảng mã đích (Target charset).
+ 1 byte tiếp theo chứa bảng mã. Con số này có thể lớn hơn 65, khi đó ta phải trừ đi 65 để ra được một mã số. Mỗi mã số sẽ tương ứng với một bảng mã.

Hình ảnh
Hình ảnh

Danh sách các loại bảng mã các bạn có thể xem trong mã nguồn đính kèm (trong tập tin Constants.cpp).
Ngoài 2 loại này ra còn có 1 bản ghi chứa thông tin về bảng mã mặc định (Default charset) có cùng cấu trúc với loại bản ghi này, nhưng loại bản ghi là 0x0, 2 byte đầu tiên chứa 0x0008.
Nếu mã loại bản ghi thông tin chung là 0x0011 (tất nhiên loại bản ghi vẫn là 0x3), đây là bản ghi chứa một số thông tin mở rộng, trong đó nếu byte thứ 0x04 chứa giá trị 0x80 thì toàn bộ từ điển sử dụng bảng mã UTF-8.

Hình ảnh

+ Source charset thì dùng cho việc chuyển đổi bảng mã của đề mục (headword) mỗi mục từ.
+ Target charset thì dùng cho việc chuyển đổi bảng mã của những thông tin chung (như tiêu đề, tác giả...) và phần định nghĩa (definition) mỗi mục từ.
+ Default charset dùng để thay thế cho Source charset và Target charset nếu không có.
Tất cả nội dung sẽ được chuyển đổi từ bảng mã này về một bảng mã thống nhất (thường là UTF-8).


d) Bản ghi chứa thông tin về ngôn ngữ (language):
+ 2 byte đầu chứa mã loại bản ghi thông tin chung: 0x0007 với ngôn ngữ nguồn (Source language) và 0x0008 với ngôn ngữ đích (Target language).
+ 4 byte tiếp theo chứa mã ngôn ngữ. Danh sách các ngôn ngữ các bạn có thể xem trong mã nguồn đính kèm (trong tập tin Constants.cpp).

Hình ảnh
(Hình trên cho thấy ngôn ngữ nguồn có mã 0x00000011, tương ứng với một ngôn ngữ nào đó (ở đây do Babylon chưa định nghĩa tiếng Việt nên tiếng Việt sẽ được định nghĩa chung là Other – một ngôn ngữ khác). Ngôn ngữ đích có mã 0x00000000, tương ứng với tiếng Anh)


2. Bản ghi lưu trữ các tài nguyên (resource) đính kèm từ điển: đây là các bản ghi thuộc loại 0x2, chứa các loại tài nguyên đính kèm chẳng hạn như ảnh minh hoạ trong từ điển.
+ 1 byte đầu tiên chứa độ dài tên tài nguyên.
+ Các byte tiếp theo (có số lượng như trên) chứa tên tài nguyên.
+ Các byte tiếp theo đến kết thúc bản ghi là nội dung của tài nguyên.

Hình ảnh
(Hình trên cho thấy loại bản ghi là 0x2 – bản ghi lưu trữ các tài nguyên đính kèm, kích thước bản ghi được lưu ở 0x1 + 1 = 2 byte tiếp theo (tức là 0x0BC3). Byte 0x80 chứa giá trị 0x0C chính là độ dài phần tên phía sau (8EAF66FD.bmp). Bắt đầu từ byte 0x8D đến hết bản ghi là phần dữ liệu của tài nguyên. Hình trên chỉ là phần đầu của bản ghi, do bản ghi có kích thước khá lớn, không thể chụp lại đầy đủ được)


3. Các bản ghi mục từ (entry): đây là các bản ghi chứa các mục từ của từ điển, và tất nhiên là các bản ghi quan trọng nhất. Có 2 loại bản ghi mục từ trong một tập tin từ điển, đó là loại mục từ ngắn (Short entry) và loại mục từ dài (Long entry). Loại bản ghi mục từ ngắn được sử dụng đa số các trường hợp, do nó được sử dụng từ những phiên bản đầu tiên của tập tin .BGL. Tuy nhiên sau một thời gian phát triển, do nhu cầu lưu trữ những mục từ có nội dung lớn hơn 2^16 - 1 byte nên Babylon đã thêm vào những bản ghi mục từ dài, dành cho một vài mục từ cần thiết (chẳng hạn một số từ nhiều nghĩa trong tiếng Anh như make, go, break, get...).

a) Bản ghi mục từ ngắn (Short entry): lưu trữ thông tin của một mục từ với phần định nghĩa (definition) có kích thước lưu trữ được trong 2 byte (tức là kích thước nhỏ hơn 2^16). Loại bản ghi: 0x1 hoặc 0xA.
+ 1 byte đầu tiên chứa độ dài của đề mục (headword), gọi giá trị này là h.
+ h byte tiếp theo chứa nội dung của đề mục.
+ 2 byte tiếp theo chứa độ dài phần định nghĩa (definition), gọi giá trị này là d.
+ d byte tiếp theo chứa nội dung phần định nghĩa. Nội dung phần định nghĩa chứa một số byte đặc biệt, có lẽ là để đánh dấu một vài thông tin gì đó nhưng chưa tìm hiểu được kỹ lắm.
Nếu sau khi đọc xong phần định nghĩa mà nội dung của bản ghi vẫn còn, thì phần sau sẽ chứa danh sách các từ cùng sử dụng mục từ này, ta tạm gọi là những đề mục dẫn xuất (derived headword).
Cấu trúc của các đề mục dẫn xuất này cũng giống như cấu trúc của đề mục chính, gồm 1 byte đầu chứa độ dài h và h byte sau chứa nội dung đề mục. Các đề mục dẫn xuất này xếp liên tiếp cho đến hết bản ghi, tức là nếu đọc xong một đề mục dẫn xuất mà bản ghi vẫn còn dữ liệu thì ta đọc tiếp đề mục dẫn xuất tiếp theo cho đến khi hết dữ liệu thì thôi.

Hình ảnh
(Hình trên là một bản ghi mục từ ngắn, bắt đầu từ byte 0x04AD45 đến byte 0x04AE56. 3 byte đầu là phần đầu của bản ghi, các bạn đã hiểu chúng. Byte 0x04AD48 chứa giá trị 0x0A, tức là đề mục có độ dài 0x0A. Lần lượt 0x0A byte tiếp theo sẽ là nội dung đề mục. Kế tiếp, tức là byte 0x04AD53 và 0x04AD54 chứa giá trị 0x0102 là độ dài phần định nghĩa. Tiếp theo là nội dung phần định nghĩa. Bản ghi mục từ này không chứa danh sách các đề mục dẫn xuất, do phần định nghĩa vừa khít phần dữ liệu bản ghi)

Sau đây ta sẽ xét thêm một ví dụ nữa, nhưng đặt trọng tâm vào phần danh sách các đề mục dẫn xuất.

Hình ảnh
(Hình trên là đoạn cuối của một bản ghi mục từ ngắn, phần đánh dấu là phần danh sách các đề mục dẫn xuất. Sau khi kết thúc phần định nghĩa ở byte 0x0521, do dữ liệu của bản ghi chưa hết, chúng ta sẽ đọc được danh sách các đề mục dẫn xuất. Ví dụ byte đầu tiên là 0x1B, tức là đề mục này sẽ có độ dài 0x1B. 0x1B byte tiếp theo chứa nội dung đề mục, là "break away (from something)". Sau đó, do vẫn chưa hết dữ liệu, ta lại có đề mục thứ 2. Cứ lần lượt như vậy cho đến khi hết dữ liệu thì thôi. Một tập tin .BGL chuẩn sẽ không xảy ra trường hợp dữ liệu bị thiếu)


b) Bản ghi mục từ dài (Long entry): lưu trữ thông tin của một mục từ với phần định nghĩa (definition) có độ dài lớn hơn 2 byte dữ liệu. Đây là loại bản ghi mới được cho vào để đáp ứng nhu cầu lưu trữ những mục từ dài, có nhiều nghĩa. Loại bản ghi: 0xB.
+ 4 byte đầu chứa giá trị 0x00000000. Có lẽ đây là số lượng một thông tin gì đó được lưu trữ phía sau, nhưng vì số lượng mẫu thử (tập tin .BGL) không nhiều nên hiện tại mình chưa gặp trường hợp nào 4 byte này có giá trị khác 0, vì thế cũng không thể biết được ý nghĩa của 4 byte này.
+ 1 byte tiếp theo là độ dài h của đề mục.
+ h byte tiếp theo là nội dung đề mục.
Tiếp theo là phần danh sách các đề mục dẫn xuất. Bản ghi loại này mới được cho vào nên có lẽ cấu trúc của nó có phần "đẹp" hơn loại bản ghi ngắn.
+ 4 byte tiếp theo là số lượng các đề mục dẫn xuất (chính vì 4 byte này mà mình còn rất băn khoăn về 4 byte đầu tiên của bản ghi, không rõ là nó lưu số lượng cái gì).
+ Tiếp theo là danh sách các đề mục dẫn xuất. Mỗi đề mục dẫn xuất có cấu trúc là 4 byte đầu chứa độ dài h và h byte tiếp theo sẽ là nội dung đề mục.
Tiếp theo là phần định nghĩa.
+ 4 byte tiếp theo chứa độ dài d của phần định nghĩa.
+ d byte tiếp theo chứa nội dung phần định nghĩa.
+ 4 byte cuối cùng của bản ghi chứa giá trị 0x00000000. Cũng giống như 4 byte đầu tiên, mình rất nghi ngờ đây là số lượng một loạt thông tin gì đó được lưu trữ phía sau. Nhưng tất cả các mẫu thử của mình có thì giá trị này đều bằng 0.

Hình ảnh
(Hình trên là phần đầu của một bản ghi mục từ dài đã xoá đi phần đầu của bản ghi. Chúng là có thể thấy 4 byte đầu 0x00000000. Tiếp đến là độ dài đề mục (0x05). 5 byte tiếp theo là đề mục "break". Tiếp đến là giá trị 0x00000001 cho biết có 1 đề mục dẫn xuất. Đề mục dẫn xuất này bao gồm độ dài 0x0000000E và nội dung "break of serve". Sau đó là độ dài phần định nghĩa, đó là 0x000104F7, và kế tiếp là nội dung của phần định nghĩa)

Hình ảnh
(Hình trên là phần cuối của một bản ghi mục từ dài. Ở cuối của loại bản ghi này sau khi kết thúc phần định nghĩa chỉ còn 4 byte có nội dung 0x00000000)


Trên đây là toàn bộ những gì mình đã nghiên cứu được từ định dạng .BGL. Mình cũng đã viết được phần lớn mã nguồn cần thiết để đọc định dạng này trên C++ dưới dạng thư viện (trước mình đã định viết trên .NET bằng ngôn ngữ lập trình C#, tuy nhiên tốc độ đọc dữ liệu quá chậm (chậm hơn gần 5 lần) so với dùng C++ mặc dù đã sử dụng tối đa những khối unsafe nên mình đã quyết định sẽ viết thư viện trên C++). Các bạn có thể tham khảo mã nguồn của mình (chưa hoàn thiện) để hiểu rõ hơn kiến trúc tập tin .BGL. Mã nguồn bao gồm thư mục BGL chứa thư viện giúp truy xuất tập tin .BGL và thư mục Analysis chứa một chương trình nhỏ để sử dụng thư viện này phân tích một tập tin .BGL nào đó. Toàn bộ cấu trúc tập tin cũng được mình chú thích đầy đủ trong mã nguồn (bằng tiếng Anh), các bạn có thể vừa đọc mã nguồn vừa đọc chú thích.
Rất mong sẽ nhận được ý kiến đóng góp cũng như (tốt hơn nữa) là ai đó nhiệt tình theo đuổi dự án này với mình.


Tác giả: Đỗ Việt Hoà – Dazzling V.I.T
Rất mong các bạn nếu chia sẻ bài viết hoặc mã nguồn này thì giữ nguyên phần thông tin tác giả cũng như ghi rõ nguồn là http://vit.launchar.com. Xin cảm ơn các bạn.
Nguồn: http://vit.launchar.com/2011/08/cau-tru ... n-cua.html



Quay về “Dự án mã nguồn mở”

Đang trực tuyến

Đang xem chuyên mục này: Không có thành viên nào trực tuyến.1 khách