Tương đương Unicode cho \ w và \ b trong các biểu thức chính quy Java?


126

Nhiều triển khai regex hiện đại diễn giải tốc \wký lớp nhân vật là "bất kỳ chữ cái, chữ số hoặc dấu chấm câu kết nối" (thường là: gạch dưới). Bằng cách đó, một regex như \w+các trận đấu từ thích hello, élève, GOÄ_432hoặc gefräßig.

Thật không may, Java thì không. Trong Java, \wđược giới hạn ở [A-Za-z0-9_]. Điều này làm cho các từ phù hợp như những từ được đề cập ở trên trở nên khó khăn, trong số các vấn đề khác.

Nó cũng xuất hiện rằng \bdấu phân cách từ khớp với những nơi không nên.

Điều gì sẽ tương đương chính xác với một .NET giống như, nhận biết Unicode \whoặc \btrong Java? Những phím tắt nào khác cần "viết lại" để làm cho chúng nhận biết Unicode?


3
Câu chuyện ngắn, Tim, là tất cả họ đều cần viết để đưa chúng phù hợp với Unicode. Tôi vẫn không thấy dấu hiệu nào cho thấy Java 1.7 sẽ làm được gì nhiều hơn với các thuộc tính Unicode hơn là cuối cùng đã thêm hỗ trợ cho các tập lệnh, nhưng đó là nó. Có một số điều bạn thực sự không thể làm nếu không truy cập tốt hơn vào phần bổ sung đầy đủ các thuộc tính Unicode. Nếu bạn chưa có tập lệnh unipropsunichars của tôi (và uninames ), thì chúng sẽ mở mắt tuyệt vời cho tất cả điều này.
tchrist

Người ta có thể xem xét thêm dấu vào lớp từ. Vì ví dụ & auml; có thể được biểu thị bằng Unicode dưới dạng \ u0061 \ u0308 hoặc \ u00E4.
Mostowski sụp đổ

3
Này Tim, hãy xem CẬP NHẬT của tôi. Họ đã thêm một lá cờ để làm cho tất cả hoạt động. Tiếng hoan hô!
tchrist

Câu trả lời:


240

Mã nguồn

Mã nguồn cho các chức năng viết lại mà tôi thảo luận dưới đây có sẵn ở đây .

Cập nhật trong Java 7

PatternLớp cập nhật của Sun cho JDK7 có một lá cờ mới tuyệt vời UNICODE_CHARACTER_CLASS, khiến mọi thứ hoạt động trở lại. Nó có sẵn dưới dạng nhúng (?U)cho bên trong mẫu, vì vậy bạn cũng có thể sử dụng nó với các Stringhàm bao của lớp. Nó cũng thể thao sửa các định nghĩa cho các tính chất khác nhau, quá. Hiện tại, nó theo dõi Tiêu chuẩn Unicode, trong cả RL1.2RL1.2a từ UTS # 18: Biểu thức chính quy Unicode . Đây là một cải tiến thú vị và ấn tượng, và nhóm phát triển sẽ được khen ngợi cho nỗ lực quan trọng này.


Các vấn đề về Regex Unicode của Java

Vấn đề với Java regexes là Perl 1.0 charclass thoát - có nghĩa là \w, \b, \s, \dvà bổ sung của họ - không phải là trong Java mở rộng để làm việc với Unicode. Một mình trong số này, \bthích một số ngữ nghĩa mở rộng nhất định, nhưng những bản đồ này không \w, cũng không phải là định danh Unicode , cũng như các thuộc tính ngắt dòng Unicode .

Ngoài ra, các thuộc tính POSIX trong Java được truy cập theo cách này:

POSIX syntax    Java syntax

[[:Lower:]]     \p{Lower}
[[:Upper:]]     \p{Upper}
[[:ASCII:]]     \p{ASCII}
[[:Alpha:]]     \p{Alpha}
[[:Digit:]]     \p{Digit}
[[:Alnum:]]     \p{Alnum}
[[:Punct:]]     \p{Punct}
[[:Graph:]]     \p{Graph}
[[:Print:]]     \p{Print}
[[:Blank:]]     \p{Blank}
[[:Cntrl:]]     \p{Cntrl}
[[:XDigit:]]    \p{XDigit}
[[:Space:]]     \p{Space}

Đây là một mớ hỗn độn thật, bởi vì nó có nghĩa là điều thích Alpha, LowerSpacelàm không trong bản đồ Java với Unicode Alphabetic, Lowercasehoặc Whitespacetài sản. Điều này là cực kỳ khó chịu. Hỗ trợ thuộc tính Unicode của Java hoàn toàn không có niên đại , theo tôi, nó có nghĩa là nó hỗ trợ không có thuộc tính Unicode nào xuất hiện trong thập kỷ qua.

Không thể nói về khoảng trắng đúng cách là siêu khó chịu. Hãy xem xét bảng sau. Đối với mỗi điểm mã đó, có cả cột kết quả J cho Java và cột kết quả P cho Perl hoặc bất kỳ công cụ regex dựa trên PCRE nào khác:

             Regex    001A    0085    00A0    2029
                      J  P    J  P    J  P    J  P
                \s    1  1    0  1    0  1    0  1
               \pZ    0  0    0  0    1  1    1  1
            \p{Zs}    0  0    0  0    1  1    0  0
         \p{Space}    1  1    0  1    0  1    0  1
         \p{Blank}    0  0    0  0    0  1    0  0
    \p{Whitespace}    -  1    -  1    -  1    -  1
\p{javaWhitespace}    1  -    0  -    0  -    1  -
 \p{javaSpaceChar}    0  -    0  -    1  -    1  -

Thấy chưa

Hầu như mọi kết quả trong khoảng trắng của Java là ̲w̲r̲o̲n̲g̲ theo Unicode. Đó là một vấn đề thực sự lớn. Java chỉ bị rối tung, đưa ra các câu trả lời là Sai sai theo thực tiễn hiện có và cũng theo Unicode. Plus Java thậm chí không cung cấp cho bạn quyền truy cập vào các thuộc tính Unicode thực sự! Trong thực tế, Java không hỗ trợ bất kỳ thuộc tính nào tương ứng với khoảng trắng Unicode.


Giải pháp cho tất cả những vấn đề đó, và nhiều hơn nữa

Để giải quyết vấn đề này và nhiều vấn đề liên quan khác, hôm qua tôi đã viết một hàm Java để viết lại một chuỗi mẫu viết lại 14 lần thoát này.

\w \W \s \S \v \V \h \H \d \D \b \B \X \R

bằng cách thay thế chúng bằng những thứ thực sự hoạt động để phù hợp với Unicode theo cách có thể dự đoán và nhất quán. Nó chỉ là một nguyên mẫu alpha từ một phiên hack duy nhất, nhưng nó hoàn toàn hoạt động.

Câu chuyện ngắn là mã của tôi viết lại 14 cái đó như sau:

\s => [\u0009-\u000D\u0020\u0085\u00A0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]
\S => [^\u0009-\u000D\u0020\u0085\u00A0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]

\v => [\u000A-\u000D\u0085\u2028\u2029]
\V => [^\u000A-\u000D\u0085\u2028\u2029]

\h => [\u0009\u0020\u00A0\u1680\u180E\u2000-\u200A\u202F\u205F\u3000]
\H => [^\u0009\u0020\u00A0\u1680\u180E\u2000\u2001-\u200A\u202F\u205F\u3000]

\w => [\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]]
\W => [^\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]]

\b => (?:(?<=[\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]])(?![\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]])|(?<![\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]])(?=[\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]]))
\B => (?:(?<=[\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]])(?=[\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]])|(?<![\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]])(?![\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]]))

\d => \p{Nd}
\D => \P{Nd}

\R => (?:(?>\u000D\u000A)|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029])

\X => (?>\PM\pM*)

Một số điều cần xem xét ...

  • Điều đó sử dụng cho \Xđịnh nghĩa của nó, cái mà Unicode bây giờ gọicụm grapheme kế thừa , không phải là cụm grapheme mở rộng , vì cái sau này khá phức tạp. Bản thân Perl hiện sử dụng phiên bản fancier, nhưng phiên bản cũ vẫn hoàn toàn khả thi cho các tình huống phổ biến nhất. EDIT: Xem phụ lục ở phía dưới.

  • Phải làm gì \dphụ thuộc vào ý định của bạn, nhưng mặc định là định nghĩa Uniode. Tôi có thể thấy mọi người không phải lúc nào cũng muốn \p{Nd}, nhưng đôi khi [0-9]hoặc \pN.

  • Hai định nghĩa ranh giới, \b\B, được viết riêng để sử dụng \wđịnh nghĩa.

  • \wĐịnh nghĩa đó là quá rộng, bởi vì nó lấy các chữ cái không chỉ là những chữ được khoanh tròn. Other_AlphabeticThuộc tính Unicode không có sẵn cho đến JDK7, vì vậy đó là cách tốt nhất bạn có thể làm.


Khám phá ranh giới

Ranh giới đã trở thành một vấn đề kể từ khi Larry Wall lần đầu tiên đưa ra \b\Bcú pháp để nói về chúng cho Perl 1.0 vào năm 1987. Chìa khóa để hiểu cách thức \b\Bcả hai hoạt động là xua tan hai huyền thoại phổ biến về chúng:

  1. Họ chỉ tìm kiếm các \wký tự từ, không bao giờ tìm các ký tự không phải từ.
  2. Họ không đặc biệt tìm kiếm các cạnh của chuỗi.

Một \branh giới có nghĩa là:

    IF does follow word
        THEN doesn't precede word
    ELSIF doesn't follow word
        THEN does precede word

Và tất cả đều được định nghĩa hoàn toàn đơn giản là:

  • theo từ(?<=\w).
  • đi trước từ(?=\w).
  • không theo từ(?<!\w).
  • không có từ trước(?!\w).

Do đó, vì IF-THENđược mã hóa dưới dạng and ed-together ABtrong regexes, an orX|Yvà bởi vì andmức độ ưu tiên cao hơn or, điều đó chỉ đơn giản AB|CD. Vì vậy, mọi \bđiều đó có nghĩa là một ranh giới có thể được thay thế một cách an toàn bằng:

    (?:(?<=\w)(?!\w)|(?<!\w)(?=\w))

với \wđịnh nghĩa theo cách thích hợp.

(Bạn có thể nghĩ nó lạ mà AClinh kiện là đối lập Trong một thế giới hoàn hảo, bạn sẽ có thể viết đó. AB|D, Nhưng trong một thời gian tôi đã đuổi theo xuống mâu thuẫn loại trừ lẫn nhau trong các thuộc tính Unicode - mà tôi nghĩ rằng tôi đã đưa về chăm sóc , nhưng tôi đã để điều kiện kép trong ranh giới chỉ trong trường hợp. Thêm vào đó, điều này làm cho nó mở rộng hơn nếu bạn có thêm ý tưởng sau này.)

Đối với các \Bphi giới hạn, logic là:

    IF does follow word
        THEN does precede word
    ELSIF doesn't follow word
        THEN doesn't precede word

Cho phép tất cả các trường hợp \Bđược thay thế bằng:

    (?:(?<=\w)(?=\w)|(?<!\w)(?!\w))

Đây thực sự là cách \b\Bhành xử. Các mẫu tương đương cho chúng là

  • \bsử dụng ((IF)THEN|ELSE)cấu trúc là(?(?<=\w)(?!\w)|(?=\w))
  • \Bsử dụng ((IF)THEN|ELSE)cấu trúc là(?(?=\w)(?<=\w)|(?<!\w))

Nhưng các phiên bản chỉ AB|CDổn, đặc biệt nếu bạn thiếu các mẫu có điều kiện trong ngôn ngữ regex của bạn - như Java. ☹

Tôi đã xác minh hành vi của các ranh giới bằng cách sử dụng cả ba định nghĩa tương đương với bộ kiểm tra kiểm tra 110.385.408 trận đấu mỗi lần chạy và tôi đã chạy trên hàng tá cấu hình dữ liệu khác nhau theo:

     0 ..     7F    the ASCII range
    80 ..     FF    the non-ASCII Latin1 range
   100 ..   FFFF    the non-Latin1 BMP (Basic Multilingual Plane) range
 10000 .. 10FFFF    the non-BMP portion of Unicode (the "astral" planes)

Tuy nhiên, mọi người thường muốn một loại ranh giới khác. Họ muốn một cái gì đó là khoảng trắng và nhận biết cạnh của chuỗi:

  • cạnh trái như(?:(?<=^)|(?<=\s))
  • cạnh phải như(?=$|\s)

Sửa lỗi Java với Java

Mã tôi đã đăng trong câu trả lời khác của tôi cung cấp điều này và khá nhiều tiện ích khác. Điều này bao gồm các định nghĩa cho các từ ngôn ngữ tự nhiên, dấu gạch ngang, dấu gạch ngang và dấu nháy đơn, cộng với một chút nữa.

Nó cũng cho phép bạn chỉ định các ký tự Unicode trong các điểm mã logic, không phải trong các đại diện UTF-16 ngu ngốc. Thật khó để nhấn mạnh tầm quan trọng của nó! Và đó chỉ là để mở rộng chuỗi.

Để thay thế charex regex làm cho lớp char trong regex Java của bạn cuối cùng hoạt động trên Unicode và hoạt động chính xác, hãy lấy toàn bộ nguồn từ đây . Bạn có thể làm với nó như bạn muốn, tất nhiên. Nếu bạn sửa nó, tôi rất muốn nghe về nó, nhưng bạn không phải làm thế. Nó khá ngắn. Các can đảm của chức năng viết lại biểu thức chính là đơn giản:

switch (code_point) {

    case 'b':  newstr.append(boundary);
               break; /* switch */
    case 'B':  newstr.append(not_boundary);
               break; /* switch */

    case 'd':  newstr.append(digits_charclass);
               break; /* switch */
    case 'D':  newstr.append(not_digits_charclass);
               break; /* switch */

    case 'h':  newstr.append(horizontal_whitespace_charclass);
               break; /* switch */
    case 'H':  newstr.append(not_horizontal_whitespace_charclass);
               break; /* switch */

    case 'v':  newstr.append(vertical_whitespace_charclass);
               break; /* switch */
    case 'V':  newstr.append(not_vertical_whitespace_charclass);
               break; /* switch */

    case 'R':  newstr.append(linebreak);
               break; /* switch */

    case 's':  newstr.append(whitespace_charclass);
               break; /* switch */
    case 'S':  newstr.append(not_whitespace_charclass);
               break; /* switch */

    case 'w':  newstr.append(identifier_charclass);
               break; /* switch */
    case 'W':  newstr.append(not_identifier_charclass);
               break; /* switch */

    case 'X':  newstr.append(legacy_grapheme_cluster);
               break; /* switch */

    default:   newstr.append('\\');
               newstr.append(Character.toChars(code_point));
               break; /* switch */

}
saw_backslash = false;

Dù sao, mã đó chỉ là một bản phát hành alpha, thứ tôi đã hack vào cuối tuần qua. Nó sẽ không ở lại như vậy.

Đối với bản beta tôi dự định:

  • cùng nhau nhân đôi mã

  • cung cấp một giao diện rõ ràng hơn về thoát thoát chuỗi không định hình so với thoát thoát regex

  • cung cấp một số linh hoạt trong việc \dmở rộng và có thể\b

  • cung cấp các phương thức tiện lợi để xử lý việc quay vòng và gọi Pattern.compile hoặc String.matches hoặc whatnot cho bạn

Để phát hành sản xuất, cần có javadoc và bộ kiểm tra JUnit. Tôi có thể bao gồm gigatester của tôi, nhưng nó không được viết dưới dạng thử nghiệm JUnit.


Phụ lục

Tôi có tin tốt và tin xấu.

Tin vui là giờ đây tôi đã có một xấp xỉ rất gần với cụm grapheme mở rộng để sử dụng cho một cải tiến \X.

Tin xấu là mô hình đó là:

(?:(?:\u000D\u000A)|(?:[\u0E40\u0E41\u0E42\u0E43\u0E44\u0EC0\u0EC1\u0EC2\u0EC3\u0EC4\uAAB5\uAAB6\uAAB9\uAABB\uAABC]*(?:[\u1100-\u115F\uA960-\uA97C]+|([\u1100-\u115F\uA960-\uA97C]*((?:[[\u1160-\u11A2\uD7B0-\uD7C6][\uAC00\uAC1C\uAC38]][\u1160-\u11A2\uD7B0-\uD7C6]*|[\uAC01\uAC02\uAC03\uAC04])[\u11A8-\u11F9\uD7CB-\uD7FB]*))|[\u11A8-\u11F9\uD7CB-\uD7FB]+|[^[\p{Zl}\p{Zp}\p{Cc}\p{Cf}&&[^\u000D\u000A\u200C\u200D]]\u000D\u000A])[[\p{Mn}\p{Me}\u200C\u200D\u0488\u0489\u20DD\u20DE\u20DF\u20E0\u20E2\u20E3\u20E4\uA670\uA671\uA672\uFF9E\uFF9F][\p{Mc}\u0E30\u0E32\u0E33\u0E45\u0EB0\u0EB2\u0EB3]]*)|(?s:.))

mà trong Java bạn sẽ viết là:

String extended_grapheme_cluster = "(?:(?:\\u000D\\u000A)|(?:[\\u0E40\\u0E41\\u0E42\\u0E43\\u0E44\\u0EC0\\u0EC1\\u0EC2\\u0EC3\\u0EC4\\uAAB5\\uAAB6\\uAAB9\\uAABB\\uAABC]*(?:[\\u1100-\\u115F\\uA960-\\uA97C]+|([\\u1100-\\u115F\\uA960-\\uA97C]*((?:[[\\u1160-\\u11A2\\uD7B0-\\uD7C6][\\uAC00\\uAC1C\\uAC38]][\\u1160-\\u11A2\\uD7B0-\\uD7C6]*|[\\uAC01\\uAC02\\uAC03\\uAC04])[\\u11A8-\\u11F9\\uD7CB-\\uD7FB]*))|[\\u11A8-\\u11F9\\uD7CB-\\uD7FB]+|[^[\\p{Zl}\\p{Zp}\\p{Cc}\\p{Cf}&&[^\\u000D\\u000A\\u200C\\u200D]]\\u000D\\u000A])[[\\p{Mn}\\p{Me}\\u200C\\u200D\\u0488\\u0489\\u20DD\\u20DE\\u20DF\\u20E0\\u20E2\\u20E3\\u20E4\\uA670\\uA671\\uA672\\uFF9E\\uFF9F][\\p{Mc}\\u0E30\\u0E32\\u0E33\\u0E45\\u0EB0\\u0EB2\\u0EB3]]*)|(?s:.))";

Tschüß!


10
Thật đáng kinh ngạc. Cảm ơn rất nhiều.
Tim Pietzcker

9
Chúa ơi, đó là một câu trả lời khai sáng. Tôi chỉ không nhận được tài liệu tham khảo Jon Skeet. Anh ta phải làm gì với điều này?
BalusC

12
@BalusC: Đó là một lời giới thiệu với Jon trước đó rằng anh ấy sẽ để tôi đưa ra câu hỏi. Nhưng làm ơn, đừng bỏ qua t@tchrist. Nó có thể đi vào đầu tôi. :)
tchrist

3
Bạn đã nghĩ về việc thêm nó vào OpenJDK chưa?
Martijn Verburg

2
@Martijn: Tôi đã không, không; Tôi không biết đó là người mở ra. :) Nhưng tôi đã nghĩ về việc phát hành nó theo nghĩa trang trọng hơn; những người khác trong bộ phận của tôi muốn thấy điều đó được thực hiện (với một số loại giấy phép nguồn mở, có thể là BSD hoặc ASL). Có lẽ tôi sẽ thay đổi API từ nguyên mẫu alpha này, dọn sạch mã, v.v. Nhưng nó giúp chúng tôi rất nhiều, và chúng tôi nghĩ rằng nó cũng sẽ giúp người khác. Tôi thực sự mong muốn Sun sẽ làm một cái gì đó về thư viện của họ, nhưng Oracle không có niềm tin.
tchrist

15

Thật đáng tiếc khi \wnó không hoạt động. Giải pháp đề xuất \p{Alpha}cũng không hiệu quả với tôi.

Có vẻ như [\p{L}]bắt tất cả các chữ Unicode. Vì vậy, tương đương Unicode \wphải là [\p{L}\p{Digit}_].


Nhưng \wcũng phù hợp với chữ số và nhiều hơn nữa. Tôi nghĩ chỉ cần thư, \p{L}sẽ làm việc.
Tim Pietzcker

Bạn đúng. \p{L}Là đủ. Ngoài ra tôi nghĩ rằng chỉ có các chữ cái là vấn đề. [\p{L}\p{Digit}_]nên bắt tất cả các ký tự chữ và số bao gồm cả dấu gạch dưới.
musiKk

@MusicKk: Xem câu trả lời của tôi để có giải pháp hoàn chỉnh cho phép bạn viết các mẫu của bạn một cách bình thường, nhưng sau đó chuyển nó qua một hàm sửa lỗi lacunae của Java để nó hoạt động chính xác trên Unicode.
tchrist

Không, \wđược Unicode định nghĩa là rộng hơn nhiều so với chỉ \pLvà các chữ số ASCII, của tất cả những điều ngớ ngẩn. Bạn phải viết [\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]]nếu bạn muốn nhận biết Unicode \wcho Java - hoặc bạn chỉ có thể sử dụng unicode_charclasschức năng của tôi từ đây . Lấy làm tiếc!
tchrist

1
@Tim, vâng, đối với các chữ cái \pLkhông hoạt động (bạn không cần phải sử dụng các đạo cụ một chữ cái). Tuy nhiên, bạn hiếm khi muốn điều đó, vì bạn phải khá cẩn thận rằng trận đấu của bạn không nhận được các câu trả lời khác nhau chỉ vì dữ liệu của bạn ở dạng Chuẩn hóa Unicode D (còn gọi là NFD, nghĩa là phân tách chính tắc ) so với trong NFC (theo sau là canonical thành phần ). Một ví dụ là điểm mã U + E9 ( "é") là \pLdạng NFC, nhưng dạng NFD của nó trở thành U + 65.301, do đó khớp \pL\pM. Bạn có thể kinda làm được việc này với \X: (?:(?=\pL)\X), nhưng bạn sẽ cần phiên bản của tôi về điều đó cho Java. :(
tchrist

7

Trong Java \w\dkhông nhận biết Unicode; chúng chỉ khớp với các ký tự ASCII [A-Za-z0-9_][0-9]. Điều tương tự cũng xảy ra với \p{Alpha}bạn bè (các "lớp nhân vật" POSIX mà họ dựa trên được cho là nhạy cảm với ngôn ngữ, nhưng trong Java họ chỉ từng khớp các ký tự ASCII). Nếu bạn muốn ghép các "ký tự từ" Unicode, bạn phải đánh vần nó, ví dụ [\pL\p{Mn}\p{Nd}\p{Pc}], đối với các chữ cái, từ bổ nghĩa không khoảng cách (dấu), chữ số thập phân và dấu chấm câu.

Tuy nhiên, Java \b là người hiểu biết về Unicode; nó cũng sử dụng Character.isLetterOrDigit(ch)và kiểm tra các chữ cái có dấu, nhưng ký tự "kết nối dấu chấm câu" duy nhất mà nó nhận ra là dấu gạch dưới. EDIT: khi tôi thử mã mẫu của bạn, nó sẽ in ""élève"như vậy ( xem nó trên ideone.com ).


Tôi xin lỗi, Alan, nhưng bạn thực sự không thể nói rằng Java \blà hiểu biết về Unicode. Nó làm cho tấn và tấn sai lầm. "\u2163=", "\u24e7=""\u0301="tất cả đều không khớp với mẫu "\\b="trong Java, nhưng được cho là - như perl -le 'print /\b=/ || 0 for "\x{2163}=", "\x{24e7}=", "\x{301}="'tiết lộ. Tuy nhiên, nếu (và chỉ nếu) bạn trao đổi trong phiên bản ranh giới từ của tôi thay vì bản địa \btrong Java, thì tất cả đều hoạt động trong Java.
tchrist

@tchrist: Tôi không bình luận về \btính đúng đắn của nó, chỉ chỉ ra rằng nó hoạt động trên các ký tự Unicode (như được triển khai trong Java), không chỉ là ASCII như \wbạn bè. Tuy nhiên, nó hoạt động chính xác \u0301khi nhân vật đó được ghép với một nhân vật cơ sở, như trong e\u0301=. Và tôi không tin rằng Java sai trong trường hợp này. Làm thế nào một dấu kết hợp có thể được coi là một ký tự từ trừ khi nó là một phần của cụm grapheme với một chữ cái?
Alan Moore

3
@Alan, đây là một cái gì đó đã được làm rõ khi Unicode làm rõ các cụm grapheme bằng cách thảo luận về các cụm grapheme mở rộng so với di sản. Định nghĩa cũ của cụm grapheme, trong đó \Xviết tắt của một dấu không theo sau bởi bất kỳ số lượng dấu nào, là vấn đề, bởi vì bạn có thể mô tả tất cả các tệp là khớp /^(\X*\R)*\R?$/, nhưng bạn không thể nếu bạn có một \pMđiểm bắt đầu các tập tin, hoặc thậm chí của một dòng. Vì vậy, họ đã gửi nó để luôn khớp với ít nhất một ký tự. Nó luôn luôn làm, nhưng bây giờ nó làm cho mô hình trên hoạt động. [
Càng

2
@Alan, điều có hại hơn là bản địa của Java \blà một phần nhận biết Unicode. Xem xét kết hợp chuỗi "élève"với mẫu \b(\w+)\b. Thấy vấn đề?
tchrist

1
@tchrist: Có, không có ranh giới từ, \w+tìm thấy hai kết quả trùng khớp: lve, điều đó đủ tệ. Nhưng với ranh giới từ, nó không tìm thấy gì, vì \bnhận ra éèlà ký tự từ. Ở mức tối thiểu, \b\wnên đồng ý về những gì một nhân vật từ và những gì không.
Alan Moore
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.