2012-03-08 41 views

Trả lời

14

Tại thời điểm này, flex chỉ tạo ra máy quét 8-bit mà về cơ bản giới hạn bạn sử dụng UTF-8. Vì vậy, nếu bạn có mẫu:

肖晗 { printf ("xiaohan\n"); } 

nó sẽ hoạt động như mong đợi, vì chuỗi byte trong mẫu và đầu vào sẽ giống nhau. Điều khó khăn hơn là các lớp nhân vật. Nếu bạn muốn kết hợp một trong hai nhân vật 肖 hoặc 晗, bạn không thể viết:

[肖晗] { printf ("xiaohan/2\n"); } 

vì điều này sẽ phù hợp với mỗi người trong số các 0xe8 Sáu byte, 0x82, 0x96, 0xe6, 0x99 và 0x97, mà trong phương tiện thực hành nếu bạn cung cấp 肖晗 làm đầu vào, mẫu sẽ khớp với sáu lần. Vì vậy, trong trường hợp đơn giản này, bạn phải viết lại mẫu này thành (肖|晗).

Đối với phạm vi, Hans Åberg đã viết một tool in Haskell mà biến đổi chúng thành mẫu 8-bit:

Unicode> urToRegU8 0 0xFFFF 
[\0-\x7F]|[\xC2-\xDF][\x80-\xBF]|(\xE0[\xA0-\xBF]|[\xE1-\xEF][\x80-\xBF])[\x80-\xBF] 
Unicode> urToRegU32 0x00010000 0x001FFFFF 
\0[\x01-\x1F][\0-\xFF][\0-\xFF] 
Unicode> urToRegU32L 0x00010000 0x001FFFFF 
[\x01-\x1F][\0-\xFF][\0-\xFF]\0 

Đây không phải là đẹp, nhưng nó phải làm việc.

+0

Thêm gợi ý về giải pháp thay thế? – xiaohan2012

+0

Tôi đã sao chép trả lời của tôi từ danh sách gửi thư đến câu trả lời. –

+0

Cảm ơn. Có vẻ để truyền cảm hứng cho tôi rất nhiều! – xiaohan2012

15

Flex không hỗ trợ Unicode. Tuy nhiên, Flex hỗ trợ đầu vào nhị phân "8 bit sạch". Do đó bạn có thể viết các mẫu từ vựng phù hợp với UTF-8. Bạn có thể sử dụng các mẫu này trong các khu vực từ vựng cụ thể của ngôn ngữ nhập liệu, ví dụ như mã định danh, nhận xét hoặc chuỗi ký tự.

Điều này sẽ hoạt động tốt cho các ngôn ngữ lập trình điển hình, nơi bạn có thể khẳng định với người dùng triển khai ngôn ngữ nguồn được viết bằng ASCII/UTF-8 (và không hỗ trợ mã hóa khác, dấu chấm).

Cách tiếp cận này sẽ không hoạt động nếu máy quét của bạn phải xử lý văn bản có thể ở bất kỳ chế độ mã hóa nào. Nó cũng sẽ không hoạt động (rất tốt) nếu bạn cần thể hiện các quy tắc từ vựng đặc biệt cho các phần tử Unicode. I E. bạn cần các ký tự Unicode và các regex Unicode trong chính máy quét.

Ý tưởng là bạn có thể nhận ra một mô hình trong đó bao gồm UTF-8 byte sử dụng một quy tắc lex, (và sau đó có lẽ lấy yytext, và chuyển đổi nó ra khỏi UTF-8 hoặc ít nhất là xác nhận nó.)

Đối với một ví dụ làm việc, xem mã nguồn của ngôn ngữ TXR, đặc biệt là tập tin này: http://www.kylheku.com/cgit/txr/tree/parser.l

Cuộn xuống phần này:

ASC  [\x00-\x7f] 
ASCN [\x00-\t\v-\x7f] 
U  [\x80-\xbf] 
U2  [\xc2-\xdf] 
U3  [\xe0-\xef] 
U4  [\xf0-\xf4] 

UANY {ASC}|{U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U} 
UANYN {ASCN}|{U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U} 
UONLY {U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U} 

Như bạn có thể thấy, chúng ta có thể xác định mô hình để phù hợp với ASCII characte rs cũng như các byte bắt đầu và tiếp tục UTF-8. UTF-8 là một ký hiệu từ vựng, và đây là một trình tạo phân tích từ vựng, vì vậy ... không sao cả!

Một số giải thích: UANY nghĩa là khớp với bất kỳ ký tự nào, ASCII một byte hoặc đa byte UTF-8. UANYN có nghĩa là UANY nhưng không khớp với dòng mới. Điều này rất hữu ích cho các mã thông báo không bị gián đoạn trên các dòng, như nói một nhận xét từ # đến cuối dòng, chứa văn bản quốc tế.UONLY có nghĩa là chỉ khớp với một ký tự mở rộng UTF-8, chứ không phải ký tự ASCII. Điều này rất hữu ích cho việc viết một quy tắc lex cần loại trừ một số ký tự ASCII cụ thể (không chỉ là dòng mới) nhưng tất cả các ký tự mở rộng đều ổn.

TỪ BỎ: Lưu ý rằng quy tắc của máy quét sử dụng chức năng gọi là utf8_dup_from để chuyển các chuỗi ký tự rộng có chứa codepoint Unicode. Chức năng đó là mạnh mẽ; nó phát hiện các vấn đề như chuỗi quá dài và byte không hợp lệ và xử lý chúng đúng cách. I E. chương trình này không dựa vào các quy tắc lex này để thực hiện xác thực và chuyển đổi, chỉ để thực hiện nhận dạng từ vựng cơ bản. Các quy tắc này sẽ nhận dạng một biểu mẫu quá dài (như mã ASCII được mã hóa bằng cách sử dụng một vài byte) làm cú pháp hợp lệ, nhưng hàm chuyển đổi sẽ xử lý chúng đúng cách. Trong mọi trường hợp, tôi không mong đợi các vấn đề bảo mật liên quan đến UTF-8 trong mã nguồn của chương trình, vì bạn phải tin tưởng mã nguồn đang chạy nó (nhưng dữ liệu được chương trình xử lý có thể không đáng tin cậy!) viết một máy quét cho dữ liệu UTF-8 không đáng tin cậy, hãy cẩn thận!

+0

Chỉ cần tự hỏi, không nên định nghĩa của U4 như sau: 'U4 [\ xf0- \ xf7]' để thực sự chứa tất cả các khả năng từ 11110000 đến 11110111? – exa

+0

@exa Chú ý đến từng chi tiết! Phạm vi đầy đủ của byte sẽ cho chúng ta các điểm mã lên tới 'U + 3FFFFF'. 'F4' giới hạn thành' U + 10FFFF'. – Kaz

+0

Tôi tự hỏi liệu phương pháp đề xuất có an toàn hay không. Các mẫu TRX này bao gồm phạm vi U + D800-U + DFFF không hợp lệ (nửa thay thế UTF016 không hợp lệ Unicode) và '{U4} {U} {U} {U}' vượt quá giới hạn trên U + 10FFFF, không giống như bạn đã nói điểm mã cuối cùng phải là '\ xf4 [\ x80- \ x8f] [\ x80- \ xbf] [\ x80- \ xbf]' không phải '\ xf4 [\ x80- \ xbf] [\ x80- \ xbf] [\ x80- \ xbf] '. –

1

Tôi tự hỏi liệu phiên bản mới nhất của flex có hỗ trợ unicode không?

Nếu có, làm cách nào để sử dụng các mẫu để khớp với các ký tự tiếng Trung?

Để khớp mẫu với ký tự Trung Quốc và các điểm mã Unicode khác bằng máy phân tích từ vựng giống như Flex, bạn có thể sử dụng RE/flex lexical analyzer cho C++ tương thích ngược với Flex. RE/flex hỗ trợ Unicode và làm việc với Bison để xây dựng lexers và parsers.

Bạn có thể viết mẫu Unicode (UTF-8 và biểu thức thông thường) trong RE/thông số kỹ thuật flex như:

%option flex unicode 
%% 
[肖晗] { printf ("xiaohan/2\n"); } 
%% 

Sử dụng toàn cầu %option unicode để cho phép Unicode. Bạn cũng có thể sử dụng một modifier địa phương (?u:) để hạn chế Unicode đến một mẫu đơn (do đó mọi thứ khác vẫn là ASCII/8-bit như trong Flex):

%option flex 
%% 
(?u:[肖晗]) { printf ("xiaohan/2\n"); } 
(?u:\p{Han}) { printf ("Han character %s\n", yytext); } 
.    { printf ("8-bit character %d\n", yytext[0]); } 
%% 

Lựa chọn flex cho phép khả năng tương thích Flex, vì vậy bạn có thể sử dụng yytext, yyleng, ECHO, v.v. Nếu không có sự lựa chọn flex RE/flex hy vọng lexer phương pháp gọi: text() (hoặc str()wstr() cho std::stringstd::wstring), size() (hoặc wsize() cho chiều dài char rộng), và echo(). Các cuộc gọi phương thức RE/flex là IMHO sạch hơn, và bao gồm các hoạt động char rộng.