Недокументированный класс символов регулярного выражения Java:\p {C}
Я нашел интересное регулярное выражение в проекте Java: "[\\p{C}&&\\S]"
Я понимаю, что &&
означает "установить пересечение", а \S
- "не-whitespace", но что такое \p{C}
, и можно ли использовать?
java.util.regex.Pattern documentation не упоминает об этом. Единственный подобный класс в списке - \p{Cntrl}
, но они ведут себя по-разному: оба они совпадают с управляющими символами, но \p{C}
совпадает дважды с символами Юникода выше U + FFFF, например PILE OF POO
:
public class StrangePattern {
public static void main(String[] argv) {
// As far as I can tell, this is the simplest way to create a String
// with code points above U+FFFF.
String poo = new String(Character.toChars(0x1F4A9));
System.out.println(poo); // prints `💩`
System.out.println(poo.replaceAll("\\p{C}", "?")); // prints `??`
System.out.println(poo.replaceAll("\\p{Cntrl}", "?")); // prints `💩`
}
}
Единственное упоминание, которое я нашел где-то, здесь:
\ p {C} или \p {Other}: невидимые управляющие символы и неиспользуемые кодовые точки.
Однако \p{Other}
, похоже, не существует в Java, а совпадающие кодовые точки не используются.
Информация о моей версии Java:
$ java -version
java version "1.8.0_92"
Java(TM) SE Runtime Environment (build 1.8.0_92-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.92-b14, mixed mode)
Бонусный вопрос: каково вероятное намерение исходного шаблона, "[\\p{C}&&\\S]"
? Это происходит в методе, который проверяет строку перед ее отправкой по электронной почте: если этот шаблон сопоставляется, возникает исключение с сообщением "Неверная строка".
Ответы
Ответ 1
Погруженный в паттерны документов под поддержкой Unicode, мы находим следующее:
Этот класс находится в соответствии с уровнем 1 Unicode Technical Standard # 18: Unicode Regular Expression, а также канонические эквиваленты RL2.1.
...
Категории могут быть указаны с дополнительным префиксом Is: Both\p {L} и \p {IsL} обозначают категорию букв Unicode. То же, что и скрипты и блоки, категории также можно указать, используя ключевое слово general_category (или его короткая форма gc), как в general_category = Lu или дс = Л.
Поддерживаемые категории - это категории стандарта Unicode в версии, указанной классом Character. Названия категорий - это те определенных в Стандарте, как нормативных, так и информативных.
Из Unicode Technical Standard # 18, мы обнаруживаем, что C
определено в соответствии с любым другим значением General_Category, и эта поддержка для этого часть требований для соответствия уровня 1. Java реализует \p{C}
, потому что утверждает, что соответствует уровню 1 UTS # 18.
Вероятно, он должен поддерживать \p{Other}
, но, видимо, этого не делает.
Хуже того, что он нарушает RL1.7, необходимый для соответствия уровня 1, который требует, чтобы соответствие выполнялось по кодовой точке вместо единицы кода:
Для удовлетворения этого требования реализация должна обрабатывать полный диапазон кодовых точек Юникода, включая значения от U + FFFF до U + 10FFFF. В частности, где используется UTF-16, последовательность, состоящая из ведущего суррогата, за которым следует завершающий суррогат, должна обрабатываться как единая кодовая точка при сопоставлении.
В тестовой строке не должно быть совпадений для \p{C}
, потому что ваша тестовая строка должна быть сопоставлена как одна кодовая точка emoji с General_Category = So (другой символ) вместо двух суррогатов.
Ответ 2
Согласно https://regex101.com/,\p {C} соответствует
Невидимые управляющие символы и неиспользуемые кодовые точки
(\ должен быть экранирован, потому что строка java, поэтому строка \\p {C} является регулярным выражением \p {C})
Я предполагаю, что это "взломанная проверка строки", поскольку \p {C}, вероятно, никогда не должен появляться внутри допустимой (заполненной символом) строки, но автор должен оставить комментарий как то, что они проверили, и что они хотели проверить, как правило, две разные вещи.
Ответ 3
Все, кроме действительного двухбуквенного кода категории Unicode или одной буквы, начинающейся с кода категории Unicode, является незаконным, поскольку Java поддерживает только однобуквенные и двухбуквенные сокращения для категорий Unicode. Вот почему \p{Other}
здесь не работает.
\p{C}
дважды совпадает с символами Unicode выше U+FFFF
, такими как PILE OF POO.
Right. Java использует внутреннюю кодировку UTF-16 для символов Unicode, а 💩 кодируется как два 16-разрядных кодовых блока (0xD83D 0xDCA9
), называемых суррогатными парами (высокие суррогаты), а так как \p{C}
соответствует каждой половине отдельно
\p{Cs}
или \p{Surrogate}
: одна половина суррогатной пары в UTF-16 кодирование.
вы видите два совпадения в наборе результатов.
Каково вероятное намерение исходного шаблона, [\\p{C}&&\\S]
?
Я не вижу достаточно веской причины, но, похоже, разработчик обеспокоен персонажами в категории Other
(например, избегая спама goomojies
в теме письма), поэтому просто попытался заблокировать их.