Regex: InComosystemDiacriticalMarks là gì?


86

Đoạn mã sau rất nổi tiếng để chuyển đổi các ký tự có dấu thành Văn bản thuần túy:

Normalizer.normalize(text, Normalizer.Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+", "");

Tôi đã thay thế phương pháp "hand made" của mình bằng phương pháp này, nhưng tôi cần hiểu phần "regex" của ReplaceAll

1) "InCombiningDiacriticalMarks" là gì?
2) Tài liệu về nó ở đâu? (và tương tự?)

Cảm ơn.


Xem thêm stackoverflow.com/a/29111105/32453 dường như có nhiều "dấu kết hợp" trong unicode hơn chỉ là dấu phụ, chỉ là một ghi chú.
rogerdpack

Câu trả lời:


74

\p{InCombiningDiacriticalMarks}là một thuộc tính khối Unicode. Trong JDK7, bạn sẽ có thể viết nó bằng cách sử dụng ký hiệu hai phần \p{Block=CombiningDiacriticalMarks}, có thể rõ ràng hơn cho người đọc. Nó được ghi lại ở đây trong UAX # 44: “Cơ sở dữ liệu ký tự Unicode” .

Điều đó có nghĩa là điểm mã nằm trong một phạm vi cụ thể, một khối, đã được cấp phát để sử dụng cho những thứ có tên đó. Đây là một cách tiếp cận không tốt, bởi vì không có gì đảm bảo rằng điểm mã trong phạm vi đó là hoặc không phải là bất kỳ thứ gì cụ thể, cũng như các điểm mã bên ngoài khối đó về cơ bản không phải cùng một ký tự.

Ví dụ, có các chữ cái Latinh trong \p{Latin_1_Supplement}khối, như é, U + 00E9. Tuy nhiên, có những thứ không phải là chữ cái Latinh ở đó. Và tất nhiên cũng có các chữ cái Latinh ở khắp nơi.

Các khối gần như không bao giờ là những gì bạn muốn.

Trong trường hợp này, tôi nghi ngờ rằng bạn có thể muốn sử dụng tài sản \p{Mn}, hay còn gọi là \p{Nonspacing_Mark}. Tất cả các điểm mã trong khối Combine_Diacriticals đều thuộc loại đó. Ngoài ra còn có (kể từ Unicode 6.0.0) 1087 Nonspacing_Marks không nằm trong khối đó.

Điều đó gần giống như kiểm tra \p{Bidi_Class=Nonspacing_Mark}, nhưng không hoàn toàn, bởi vì nhóm đó cũng bao gồm các dấu bao quanh , \p{Me}. Nếu bạn muốn cả hai, bạn có thể nói [\p{Mn}\p{Me}]nếu bạn đang sử dụng một công cụ regex Java mặc định, vì nó chỉ cấp quyền truy cập vào thuộc tính General_Category.

Bạn sẽ phải sử dụng JNI để truy cập thư viện regex ICU C ++ theo cách Google làm để truy cập một cái gì đó tương tự như vậy \p{BC=NSM}, bởi vì hiện tại chỉ ICU và Perl cấp quyền truy cập vào tất cả các thuộc tính Unicode. Thư viện regex của Java thông thường chỉ hỗ trợ một vài thuộc tính Unicode tiêu chuẩn. Mặc dù vậy, trong JDK7 sẽ có hỗ trợ cho đề xuất Tập lệnh Unicode, điều này rất thích hợp cho thuộc tính Block. Vì vậy, trong JDK7 bạn có thể viết \p{Script=Latin}hoặc \p{SC=Latin}, hoặc viết tắt \p{Latin}, để lấy bất kỳ ký tự nào từ hệ thống chữ Latinh. Điều này dẫn đến rất phổ biến cần thiết [\p{Latin}\p{Common}\p{Inherited}].

Hãy lưu ý rằng điều đó sẽ không xóa những gì bạn có thể coi là dấu "trọng âm" khỏi tất cả các ký tự! Có nhiều nó sẽ không làm điều này cho. Ví dụ, bạn không thể chuyển Đ thành D hoặc ø thành o theo cách đó. Vì vậy, bạn cần giảm điểm mã xuống những điểm phù hợp với cùng độ mạnh đối chiếu chính trong Bảng đối chiếu Unicode.

Một nơi khác mà \p{Mn}điều không thành công tất nhiên là bao gồm các dấu như \p{Me}, rõ ràng, nhưng cũng có những \p{Diacritic}ký tự không phải là dấu. Đáng buồn thay, bạn cần hỗ trợ tài sản đầy đủ cho điều đó, có nghĩa là JNI cho ICU hoặc Perl. Tôi e rằng Java có rất nhiều vấn đề với hỗ trợ Unicode.

Chờ đã, tôi thấy bạn là người Bồ Đào Nha. Sau đó, bạn sẽ không gặp vấn đề gì nếu bạn chỉ xử lý văn bản tiếng Bồ Đào Nha.

Tuy nhiên, tôi cá là bạn không thực sự muốn loại bỏ dấu trọng âm, mà là bạn muốn có thể khớp những thứ “thiếu trọng âm”, phải không? Nếu vậy, bạn có thể làm như vậy bằng cách sử dụng lớp đối chiếu ICU4J (ICU cho Java) . Nếu bạn so sánh ở cường độ chính, dấu trọng âm sẽ không được tính. Tôi làm điều này mọi lúc vì tôi thường xử lý văn bản tiếng Tây Ban Nha. Tôi có một ví dụ về cách làm điều này cho người Tây Ban Nha ngồi ở đâu đó ở đây nếu bạn cần.


Vì vậy, tôi phải giả định rằng phương pháp được cung cấp trên toàn bộ web (và thậm chí ở đây tại SO) không phải là phương pháp được khuyến nghị cho "DeAccent" một từ. Tôi đã thực hiện một phương pháp đơn giản chỉ dành cho tiếng Bồ Đào Nha, nhưng thấy cách tiếp cận kỳ lạ này (và như bạn đã nói, nó hoạt động cho mục đích của tôi, nhưng vì vậy phương pháp cuối cùng của tôi đã làm!). Vì vậy, có một cách tiếp cận "được triển khai tốt" tốt hơn sẽ bao gồm hầu hết các tình huống? Một ví dụ sẽ rất hay. Cảm ơn vì đã dành thời gian cho tôi.
marcolopes

1
@Marcolopes: Tôi đã giữ nguyên dữ liệu và sử dụng Thuật toán đối chiếu Unicode để thực hiện so sánh độ bền chính. Bằng cách đó, nó chỉ so sánh các chữ cái, nhưng bỏ qua cả chữ hoa và dấu. Nó cũng cho phép những thứ đáng lẽ giống nhau cùng một chữ cái, mà việc loại bỏ các dấu chỉ là một sự gần đúng nhạt và không đạt yêu cầu. Thêm vào đó, sẽ rõ ràng hơn là không chia nhỏ dữ liệu nếu bạn có thể làm việc với nó theo cách làm những gì bạn muốn nhưng không yêu cầu.
tchrist

Câu trả lời khá hay, Tuy nhiên, có một câu hỏi, Tôi có thể sử dụng Trình chuẩn hóa trong java và sử dụng InCombiningDiacriticalMarks nhưng loại trừ một số ký tự như ü khỏi chuyển đổi thành u không?
AlexCon

6
vâng, tôi hoàn toàn hiểu tất cả những điều này
Dónal 19/09

4

Đã bắt tôi một lúc, nhưng tôi đã thu thập được tất cả:

Đây là regex nên bao gồm tất cả các ký tự zalgo bao gồm cả những ký tự bị bỏ qua trong phạm vi 'bình thường'.

([\u0300–\u036F\u1AB0–\u1AFF\u1DC0–\u1DFF\u20D0–\u20FF\uFE20–\uFE2F\u0483-\u0486\u05C7\u0610-\u061A\u0656-\u065F\u0670\u06D6-\u06ED\u0711\u0730-\u073F\u0743-\u074A\u0F18-\u0F19\u0F35\u0F37\u0F72-\u0F73\u0F7A-\u0F81\u0F84\u0e00-\u0eff\uFC5E-\uFC62])

Hy vọng điều này giúp bạn tiết kiệm thời gian.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.