Как правильно написать регулярное выражение для имени Unicode в Java? - PullRequest
4 голосов
/ 27 июня 2011

Мне нужно написать регулярное выражение, чтобы я мог заменить недопустимые символы на входе пользователя, прежде чем отправлять его дальше. Я думаю, что мне нужно использовать string.replaceAll("regex", "replacement"), чтобы сделать это. Конкретная строка кода должна заменить все символы, которые не являются буквами Юникода. Так что это белый список символов Юникода. В основном это проверка и замена недопустимых символов имени пользователя.

На данный момент я нашел следующее: \p{L}\p{M}, но я не уверен, как запустить его в регулярном выражении, чтобы оно работало, как я объяснил выше. Это будет случай отрицания регулярного выражения?

Ответы [ 2 ]

8 голосов
/ 27 июня 2011

Да, вам нужно отрицание. Регулярное выражение будет [^\p{L}] для всего, кроме букв . Другой способ написать это будет \P{L}.

\p{M} означает «все знаки», поэтому [^\p{L}\p{M}] означает ** все, что не является ни буквой, ни знаком. Это также можно записать как [\P{L}&&[\P{M}]], но на самом деле это не лучше.

В строке Java все \ должны быть удвоены, поэтому вы должны написать string.replaceAll("[^\\p{L}\\p{M}]", "replacement").


Из комментария:

Кстати, относительно вашего ответа, что попадает в категорию оценок? Мне это вообще нужно? Разве буквы не подходят для имени?

Эта категория состоит из подкатегорий

  • Mn: Марк, без пробелов

    Примером этого является ̀, U + 0300. Это АКЦЕНТ КОМБИНИРОВАННОЙ МОГИЛКИ, и его можно использовать вместе с буквой (буквой перед) для создания акцентированных символов. Для часто используемых акцентированных символов уже есть предварительно составленная форма (например, é), но для других - нет.

  • Mc: Mark, Spacing Combining.

    Это довольно редко ... Я нашел их в основном в южно-азиатских сценариях и для музыкальных нот. Например, у нас есть U + 1D165, СТРОКА СОЧЕТАНИЯ МУЗЫКАЛЬНОГО СИМВОЛА. 텦, который можно сочетать с U + 1D15D, МУЗЫКАЛЬНЫЙ СИМВОЛ ВСЕ ПРИМЕЧАНИЕ, 텝, к чему-то вроде 텝텦. (Хм, изображения здесь выглядят не так. Полагаю, мой браузер не поддерживает эти символы. Посмотрите на кодовые таблицы , если они здесь не правы.)

  • Я: Марк, вмещающий

    Это отметки, которые каким-то образом заключают в себе основную букву (предыдущую, если я правильно понимаю). Одним из примеров будет U + 20DD, ⃝, который позволяет создавать такие вещи, как A⃝. (Это должно быть отображено как A, заключенный в кружок, если я правильно понимаю. В моем браузере это не так.) Другим будет U + 20E3, ⃣, КОМБИНИРУЮЩАЯ ЗАКРЫТИЯ KEYCAP, которая должна выглядеть как крышка ключа с буквой (A (). (Они не отображаются в моем браузере. Посмотрите кодовую диаграмму , если вы их не видите.)

Вы можете найти их все, выполнив поиск в Unicode-Data.txt для ;Mn;, ;Mc; или ;Me;, соответственно. Некоторая дополнительная информация содержится в FAQ: символы и сочетания знаков .

Они вам нужны? Я не уверен здесь. Я думаю, что наиболее распространенные имена (по крайней мере, в латинских алфавитах) используют предварительно составленные буквы. Но пользователь может вводить их в разложенном виде - я думаю, что в Mac OS X это по умолчанию. Вам придется запустить алгоритм нормализации, прежде чем отфильтровывать неизвестные символы. (Запуск нормализации в любом случае кажется хорошей идеей, если вы хотите сравнить имена, а не только показать их на экране.)


Редактировать: не имеет прямого отношения к вопросу, но относится к обсуждению в комментариях:

Я написал программу быстрого тестирования, чтобы показать, что [^\pL\pM] не эквивалентно [\PL\PM]:

package de.fencing_game.paul.examples;

import java.util.regex.*;

public class RegexSample {

    static String[] regexps = {
        "[^\\pL\\pM]", "[\\PL\\PM]",
        ".", "\\pL", "\\pM",
        "\\PL", "\\PM"
    };

    static String[] strings = {
        "x", "A", "3", "\n", ".", "\t", "\r", "\f",
        " ", "-", "!", "»", "›", "‹", "«",
        "ͳ", "Θ", "Σ", "Ϫ", "Ж", "ؤ",
        "༬", "༺", "༼", "ང", "⃓", "✄",
        "⟪", "や", "゙", 
        "+", "→", "∑", "∢", "※", "⁉", "⧓", "⧻",
        "⑪", "⒄", "⒰", "ⓛ", "⓶",
        "\u0300" /* COMBINING GRAVE ACCENT, Mn */,
        "\u0BCD" /* TAMIL SIGN VIRAMA, Me */,
        "\u20DD" /* COMBINING ENCLOSING CIRCLE, Me */,
        "\u2166" /* ROMAN NUMERAL SEVEN, Nl */,
    };


    public static void main(String[] params) {
        Pattern[] patterns = new Pattern[regexps.length];

        System.out.print("       ");
        for(int i = 0; i < regexps.length; i++) {
            patterns[i] = Pattern.compile(regexps[i]);
            System.out.print("| " + patterns[i] + " ");
        }
        System.out.println();
        System.out.print("-------");
        for(int i = 0; i < regexps.length; i++) {
            System.out.print("|-" +
                             "--------------".substring(0,
                                                        regexps[i].length()) +
                             "-");
        }
        System.out.println();

        for(int j = 0; j < strings.length; j++) {
            System.out.printf("U+%04x ", (int)strings[j].charAt(0));
            for(int i = 0; i < regexps.length; i++) {
                boolean match = patterns[i].matcher(strings[j]).matches();
                System.out.print("| " + (match ? "✔" : "-")  +
                                 "         ".substring(0, regexps[i].length()));
            }
            System.out.println();
        }
    }
}

Вот вывод (с OpenJDK 1.6.0_20 на OpenSUSE):

       | [^\pL\pM] | [\PL\PM] | . | \pL | \pM | \PL | \PM 
-------|-----------|----------|---|-----|-----|-----|-----
U+0078 | -         | ✔        | ✔ | ✔   | -   | -   | ✔   
U+0041 | -         | ✔        | ✔ | ✔   | -   | -   | ✔   
U+0033 | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+000a | ✔         | ✔        | - | -   | -   | ✔   | ✔   
U+002e | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+0009 | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+000d | ✔         | ✔        | - | -   | -   | ✔   | ✔   
U+000c | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+0020 | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+002d | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+0021 | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+00bb | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+203a | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+2039 | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+00ab | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+0373 | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+0398 | -         | ✔        | ✔ | ✔   | -   | -   | ✔   
U+03a3 | -         | ✔        | ✔ | ✔   | -   | -   | ✔   
U+03ea | -         | ✔        | ✔ | ✔   | -   | -   | ✔   
U+0416 | -         | ✔        | ✔ | ✔   | -   | -   | ✔   
U+0624 | -         | ✔        | ✔ | ✔   | -   | -   | ✔   
U+0f2c | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+0f3a | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+0f3c | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+0f44 | -         | ✔        | ✔ | ✔   | -   | -   | ✔   
U+20d3 | -         | ✔        | ✔ | -   | ✔   | ✔   | -   
U+2704 | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+27ea | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+3084 | -         | ✔        | ✔ | ✔   | -   | -   | ✔   
U+3099 | -         | ✔        | ✔ | -   | ✔   | ✔   | -   
U+002b | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+2192 | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+2211 | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+2222 | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+203b | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+2049 | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+29d3 | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+29fb | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+246a | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+2484 | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+24b0 | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+24db | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+24f6 | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   
U+0300 | -         | ✔        | ✔ | -   | ✔   | ✔   | -   
U+0bcd | -         | ✔        | ✔ | -   | ✔   | ✔   | -   
U+20dd | -         | ✔        | ✔ | -   | ✔   | ✔   | -   
U+2166 | ✔         | ✔        | ✔ | -   | -   | ✔   | ✔   

Мы можем видеть, что:

  1. [^\pL\pM] равно не эквивалентно [\PL\PM]
  2. [\PL\PM] действительно соответствует всему, но
  3. still [\PL\PM] не равно ., поскольку . не соответствует \n и \r.

Второй момент вызван тем, что [\PL\PM] является объединением из \PL и \PM: \PL содержит символы из всех категорий, кроме L (включая M), и \PM содержит символы из всех категорий, кроме M (включая L) - вместе они содержат весь репертуар символов.

[^pL\pM], с другой стороны, является дополнением объединения \pL и \pM, что эквивалентно пересечению из \PL и PM.

2 голосов
/ 27 июня 2011

Я не верю, что библиотека регулярных выражений Java по умолчанию (читай: вне связи с ICU, что я бы предложил сделать, даже если для этого требуется JNI) поддерживает свойства Unicode, которые вам необходимы для этого.

Если это так, вы включите \p{Diacritic} в свой шаблон. Но для этого вам нужна полная поддержка свойств.

Полагаю, вы могли бы стрелять за (\pL\pM*)+, но это не подходит для различных диакритических знаков: что если чье-то имя не просто Étoile, а L’étoile?

Кроме того, я подумал, что проблема проверки имен людей считалась практически неразрешимой, и поэтому вы должны просто позволить людям использовать все, что им нравится, возможно, очищенные в соответствии с алгоритмом "stringprep" RFC 3454 .

...