我需要编写一个正则表达式,以便在进一步发送之前替换用户输入中的无效字符。我想我需要使用 string.replaceAll("regex", "replacement")
来做到这一点。
特定的代码行应该替换所有不是 unicode 字母的字符。所以它是一个 unicode 字符的白名单。基本上它是验证和替换用户名字的无效字符。
到目前为止我发现的是:\p{L}\p{M}
,但我不确定如何在正则表达式中启动它,这样它才能像我一样工作上面解释了。这会是正则表达式否定案例吗?
最佳答案
是的,你需要否定。正则表达式将是 [^\p{L}]
表示除字母以外的任何内容。另一种写法是 \P{L}
。
\p{M}
表示“所有标记”,因此 [^\p{L}\p{M}]
表示**任何既不是字母的东西也没有标记。这也可以写成 [\P{L}&&[\P{M}]]
,但这并不是真的更好。
在 Java 字符串中,所有 \
都必须加倍,因此您可以编写 string.replaceAll("[^\\p{L}\\p{M}]", "替换")
那里。
来自评论:
By the way, regarding to your answer, what fall in the marks category? Do I even need that? Wouldn't just letters be fine for firstname?
这个类别由子类别组成
Mn:标记,非间距
一个例子是
̀
, U+0300。这是 COMBINING GRAVE ACCENT,可以与字母(前面的字母)一起使用来创建重音字符。对于常用的重音字符,已经有一个预组合形式(例如é
),但对于其他的则没有。Mc:标记,间距组合。
这些很少见……我主要在南亚文字和音符中发现它们。例如,我们有 U+1D165,MUSICAL SYMBOL COMBINING STEM。 텦,可以与 U+1D15D,音乐符号全音符,텝 组合成 텝텦 之类的东西。 (嗯,图片在这里看起来不对。我想我的浏览器不支持这些字符。看看 code charts ,如果它们在这里是错误的。)
我:马克,封闭
这些标记以某种方式包含了基本字母(如果我理解正确的话,是前一个)。一个例子是 U+20DD,⃝,它允许创建像
A⃝
这样的东西。 (如果我理解正确的话,这应该呈现为一个圆圈包围的 A。在我的浏览器中它没有。)另一个是 U+20E3,⃣,COMBINING ENCLOSING KEYCAP,它应该看起来像一个键帽上面有字母(A⃣)。 (它们没有在我的浏览器中显示。如果看不到它们,请查看 code chart。)
您可以通过搜索 Unicode-Data.txt 找到它们。分别为 ;Mn;
、;Mc;
或 ;Me;
。更多信息在 FAQ: Characters and Combining Marks 中。 .
你需要它们吗?我在这里不确定。我认为,最常见的名字(至少在拉丁字母中)会使用预组合字母。但是用户可能会以分解的形式输入它们——我认为在 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();
}
}
}
这是输出(在 OpenSUSE 上使用 OpenJDK 1.6.0_20):
| [^\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 | ✔ | ✔ | ✔ | - | - | ✔ | ✔
我们可以看到:
[^\pL\pM]
不等同于[\PL\PM]
[\PL\PM]
确实匹配所有内容,但是- 仍然
[\PL\PM]
不等于.
,因为.
不匹配\n
和\r
。
第二点是因为[\PL\PM]
是\PL
和\的union PM
:\PL
包含除L(包括M)以外的所有类别的字符,\PM
包含除M(包括L)以外的所有类别的字符- 它们一起包含了整个角色轨道。
[^pL\pM]
是 \pL
和 \pM
并集的补集,它相当于 \PL
和 PM
的交集。
关于java - 如何在 Java 中正确编写 unicode 名字的正则表达式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6493954/