2013-04-08 22 views
29

Câu hỏi này là một phần mở rộng của Do C++11 regular expressions work with UTF-8 strings?Phạm vi của UTF-8 nhân vật trong C++ 11 Regex

#include <regex> 
if (std::regex_match ("中", std::regex("中"))) // "\u4e2d" also works 
    std::cout << "matched\n"; 

Chương trình được biên soạn trên Mac Mountain Lion với clang++ với các tùy chọn sau:

clang++ -std=c++0x -stdlib=libc++ 

Mã trên hoạt động. Đây là một phạm vi tiêu chuẩn regex "[一-龠々〆ヵヶ]" để khớp với bất kỳ ký tự Kanji hoặc Trung Quốc nào của Nhật Bản. Nó hoạt động trong Javascript và Ruby, nhưng tôi không thể có vẻ để có được phạm vi làm việc trong C++ 11, ngay cả với việc sử dụng một phiên bản tương tự [\u4E00-\u9fa0]. Mã bên dưới không khớp với chuỗi.

if (std::regex_match ("中", std::regex("[一-龠々〆ヵヶ]"))) 
    std::cout << "range matched\n"; 

Thay đổi ngôn ngữ cũng không hữu ích. Ý tưởng nào?

EDIT

Vì vậy, tôi đã phát hiện ra rằng tất cả các dãy tác dụng nếu bạn thêm một + đến cùng. Trong trường hợp này [一-龠々〆ヵヶ]+, nhưng nếu bạn thêm {1}[一-龠々〆ヵヶ]{1} thì nó không hoạt động. Hơn nữa, nó dường như vượt qua ranh giới của nó. Nó sẽ không khớp với các ký tự latin, nhưng nó sẽ khớp với \u306f\u3041. Cả hai đều nằm bên dưới \u4E00

nhahtdh cũng đề xuất regex_search cũng hoạt động mà không cần thêm + nhưng vẫn chạy cùng một vấn đề như trên bằng cách kéo các giá trị ngoài phạm vi của nó. Chơi với các địa phương một chút là tốt. Mark Ransom cho rằng nó xử lý chuỗi UTF-8 như một tập hợp các byte ngu ngốc, tôi nghĩ rằng đây có thể là những gì nó đang làm.

Tiếp tục đẩy lý thuyết cho rằng UTF-8 là nhận được lộn xộn một số cách, [a-z]{1}[a-z]+ trận a, nhưng chỉ [一-龠々〆ヵヶ]+ trận đấu bất kỳ ký tự, không [一-龠々〆ヵヶ]{1}.

+0

Trình biên dịch là gì? – nhahtdh

+0

clang ++ -std = C++ 0x -stdlib = libC++ trên Mac Mountain Lion – MCH

+0

Một số thử nghiệm và tôi đã tìm thấy một giải pháp, thêm '+' vào cuối phạm vi – MCH

Trả lời

30

Được mã hóa bằng UTF-8, chuỗi "[一-龠々〆ヵヶ]" bằng số này: "[\xe4\xb8\x80-\xe9\xbe\xa0\xe3\x80\x85\xe3\x80\x86\xe3\x83\xb5\xe3\x83\xb6]". Và đây không phải là lớp học droid bạn đang tìm kiếm.

Lớp nhân vật bạn đang tìm kiếm là một trong đó bao gồm:

  • bất kỳ ký tự nằm trong khoảng U + 4E00..U + 9FA0; hoặc
  • bất kỳ ký tự nào 々, 〆, ヵ, ヶ.

Lớp nhân vật mà bạn chỉ định là một trong đó bao gồm:

  • bất kỳ "ký tự" \ xe4 hoặc \ xb8; hoặc
  • bất kỳ "ký tự" nào trong phạm vi \ x80 .. \ xe9; hoặc
  • bất kỳ "ký tự" \ xbe, \ xa0, \ xe3, \ x80, \ x85, \ xe3 (lần nữa), \ x80 (lần nữa), \ x86, \ xe3 (lần nữa), \ x83, \ xb5, \ xe3 (lần nữa), \ x83 (lần nữa), \ xb6.

Lộn xộn phải không? Bạn có thấy vấn đề?

Điều này sẽ không khớp với các ký tự "latin" (bởi vì trong UTF-8 tất cả đều sử dụng một byte dưới 0x80 và không có ký tự nào trong lớp ký tự lộn xộn đó.

Nó sẽ không khớp với "中" hoặc vì "中" có ba "ký tự" và regex của bạn chỉ khớp với một "ký tự" trong danh sách dài kỳ lạ đó. Hãy thử assert(std::regex_match("中", std::regex("..."))) và bạn sẽ thấy.

Nếu bạn thêm + nó hoạt động vì "中" có ba trong số "ký tự" đó trong danh sách dài kỳ lạ của bạn và giờ đây regex của bạn khớp với một hoặc nhiều.

Nếu thay vào đó bạn thêm {1} thì không khớp vì chúng tôi quay lại để khớp với ba "ký tự" với một "ký tự".

Ngẫu nhiên "中" khớp với "中" vì chúng tôi khớp với ba "ký tự" với cùng ba "ký tự" trong cùng một thứ tự.

Rằng regex với + sẽ thực sự khớp với một số thứ không mong muốn vì nó không quan tâm đến trật tự. Bất kỳ ký tự nào có thể được tạo từ danh sách byte đó trong UTF-8 sẽ khớp nhau. Nó sẽ khớp với "\xe3\x81\x81" (ぁ U + 3041) và thậm chí nó sẽ khớp với đầu vào UTF-8 không hợp lệ như "\xe3\xe3\xe3\xe3".

Vấn đề lớn hơn là bạn đang sử dụng một thư viện regex thậm chí không có hỗ trợ cấp 1 cho Unicode, yêu cầu tối thiểu tối thiểu. Nó nghiền byte và không có nhiều regex nhỏ quý giá của bạn có thể làm gì về nó.

Và vấn đề lớn hơn nữa là bạn đang sử dụng một bộ ký tự được mã hóa cứng để chỉ định "bất kỳ chữ Hán nào của Nhật Bản hoặc ký tự Trung Quốc". Tại sao không sử dụng thuộc tính Unicode Script cho điều đó?

R"(\p{Script=Han})"

Ồ đúng, điều này sẽ không hoạt động với C++ 11 regexes. Trong một khoảnh khắc, tôi gần như quên mất những thứ khó chịu hơn vô dụng với Unicode.

Vì vậy, bạn nên làm gì?

Bạn có thể giải mã đầu vào của mình thành std::u32string và sử dụng char32_t cho tất cả kết hợp. Điều đó sẽ không cung cấp cho bạn mess này, nhưng bạn vẫn sẽ được hardcoding phạm vi và trường hợp ngoại lệ khi bạn có nghĩa là "một tập hợp các ký tự chia sẻ một tài sản nhất định".

Tôi khuyên bạn nên quên về C++ 11 regexes và sử dụng một số thư viện biểu thức chính quy có hỗ trợ Unicode mức tối thiểu 1, giống như trong ICU.

+0

Cảm ơn bạn Martinho, đó là một bài rất thông tin. Cung cấp cho tôi và hiểu rõ hơn về UTF-8 và các biểu thức chính quy. Dù sao, tôi từ bỏ regex trong dự án này vì tôi chỉ cần biết nếu một glyph thuộc về một phạm vi cụ thể, và hơn thẻ nó vào phạm vi đó để hardcoding là một giải pháp nhanh chóng và dễ dàng. Tôi nghĩ rằng regexes sẽ là một giải pháp đơn giản và thanh lịch, nhưng tôi thấy rằng điều đó không giữ cho C++ 11. – MCH

+8

@MCH Vâng, tôi đoán lấy ICU cho một trận đấu nhỏ bé có thể là quá nhiều. Nếu bạn muốn sử dụng Unicode và regexes, Perl là khá nhiều ngôn ngữ duy nhất mà có nó nghiêm túc. Đó là trạng thái buồn, nhưng đó là những gì chúng ta có. Cá nhân tôi nghĩ rằng ' 'là một số rác trong stdlib. Đó là năm 2013 và giả vờ Unicode không tồn tại là có tính cạnh tranh và chỉ góp phần vào ý tưởng này đối phó với Unicode là quá đau đớn để chăm sóc (gợi ý: nếu búa của bạn không có đầu, bạn sẽ gặp khó khăn khi lái móng tay). –

+0

Sử dụng wregex thay thế và sử dụng http://utfcpp.sourceforge.net/ hoặc tiền tố chuỗi của bạn bằng 'L'. –