正则表达式,用于从xml模式到Java的Unicode拉丁脚本的子集

时间:2019-04-30 15:45:56

标签: java regex unicode

首先:我在阅读正则表达式和处理unicode符号方面非常不好。

在德国政府中,IT系统不能支持所有字符,而只能支持Latin_script_in_Unicode的一部分。

在官方文档中,为XML模式提供了以下正则表达式:

(([	-

 -~¡-¬®-ćĊ-ěĞ-ģĦ-ıĴ-śŞ-ūŮ-žƏƠ-ơƯ-ưƷǍ-ǔǞ-ǟǤ-ǰǴ-ǵǺ-ǿȘ-țȞ-ȟȪ-ȫȮ-ȳəʒḂ-ḃḊ-ḋḐ-ḑḞ-ḡḤ-ḧḰ-ḱṀ-ṁṄ-ṅṖ-ṗṠ-ṣṪ-ṫẀ-ẅẌ-ẓẞẠ-ầẪ-ẬẮ-ềỄ-ồỖ-ờỤ-ỹ€])|(M̂|N̂|m̂|n̂|D̂|d̂|J̌|L̂|l̂))*

我现在正尝试将此正则表达式迁移到Java,并想知道如何执行此操作。在我的第一步中,我编写了这两个测试方法,它们显然是有效的拉丁字符串,或者显然不是:

@Test
@DisplayName("OK: Just normal characters and numbers")
void testJustNormalCharacters() {
  String sut = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";

  assertTrue(RegExPruefung.matches(sut, RegEx.T_VALIDSTRINGLATIN));
}

@Test
@DisplayName("NOK: Chinese sign")
void testChineseSign() {
  String sut = "abc⺠";

  assertFalse(RegExPruefung.matches(sut, RegEx.T_VALIDSTRINGLATIN));
}

为了澄清:我将regEx保存在枚举中。测试中将调用以下方法。如您所见,它仅采用枚举值并将其放入正式的matchs方法中。对于其他正则表达式,它可以正常工作。

public static boolean matches(String stringToCheck, RegEx regExToMatch) {
  return stringToCheck.matches(regExToMatch.getRegEx());
}    

到目前为止,我已经尝试过:

1)我的第一个尝试是使用-转义\-并在字符串中使用xml-schema表达式,但这在测试中仍然给我一个假,只有字符和数字

"^(([	\\-

 \\-~¡\\-¬®\\-ćĊ\\-ěĞ\\-ģĦ\\-ıĴ\\-śŞ\\-ūŮ\\-žƏƠ\\-ơƯ\\-ưƷǍ\\-ǔǞ\\-ǟǤ\\-ǰǴ\\-ǵǺ\\-ǿȘ\\-țȞ\\-ȟȪ\\-ȫȮ\\-ȳəʒḂ\\-ḃḊ\\-ḋḐ\\-ḑḞ\\-ḡḤ\\-ḧḰ\\-ḱṀ\\-ṁṄ\\-ṅṖ\\-ṗṠ\\-ṣṪ\\-ṫẀ\\-ẅẌ\\-ẓẞẠ\\-ầẪ\\-ẬẮ\\-ềỄ\\-ồỖ\\-ờỤ\\-ỹ€])|(M̂|N̂|m̂|n̂|D̂|d̂|J̌|L̂|l̂))*$"

2)其次,我尝试将正则表达式更改为预定义的\p{isLatin},结果为^\\p{isLatin}*$,但测试仍然表明第一个字符串不是有效的拉丁字符。

如何解决此问题?

编辑: 我不认为它是"SO Java regex for support Unicode"的副本,因为我认为我的主要问题是了解如何将表达式从xml模式转换为java。不过,该线程有助于提醒我,必须以双反斜杠转义unicode“起始元素”(\u)。

1 个答案:

答案 0 :(得分:2)

您需要&#xHEX;而不是\uHEX。请注意,虽然&#xHEX;代表序列;的结尾,但是\uHEX并不具有;,而总是具有4个十六进制值,可能带有前导零。

因此,	不是表示为\u9,而是表示为\u0009

无论如何,您都可以创建 regex 工具来动态替换它们。

String originalRegex = "(([	-

 -~¡-¬®-ćĊ-ěĞ-ģĦ-ıĴ-śŞ-ūŮ-žƏƠ-ơƯ-ưƷǍ-ǔǞ-ǟǤ-ǰǴ-ǵǺ-ǿȘ-țȞ-ȟȪ-ȫȮ-ȳəʒḂ-ḃḊ-ḋḐ-ḑḞ-ḡḤ-ḧḰ-ḱṀ-ṁṄ-ṅṖ-ṗṠ-ṣṪ-ṫẀ-ẅẌ-ẓẞẠ-ầẪ-ẬẮ-ềỄ-ồỖ-ờỤ-ỹ€])|(M̂|N̂|m̂|n̂|D̂|d̂|J̌|L̂|l̂))*";

Pattern p = Pattern.compile("&#x(?<hex>[0-9a-z]{1,4});", Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(originalRegex);

StringBuffer sb = new StringBuffer();
while(m.find()){
    int decValue = Integer.parseInt(m.group("hex"), 16);
    String replacement = String.format("\\u%04x", decValue);
    m.appendReplacement(sb, Matcher.quoteReplacement(replacement)); // quoteReplacement to escape "\"
}
m.appendTail(sb);
String replacedRegex = sb.toString();
//System.out.println(replacedRegex);

这给了我们(([\u0009-\u000a\u000d\u0020-\u007e\u00a1-\u00ac\u00ae-\u0107\u010a-\u011b\u011e-\u0123\u0126-\u0131\u0134-\u015b\u015e-\u016b\u016e-\u017e\u018f\u01a0-\u01a1\u01af-\u01b0\u01b7\u01cd-\u01d4\u01de-\u01df\u01e4-\u01f0\u01f4-\u01f5\u01fa-\u01ff\u0218-\u021b\u021e-\u021f\u022a-\u022b\u022e-\u0233\u0259\u0292\u1e02-\u1e03\u1e0a-\u1e0b\u1e10-\u1e11\u1e1e-\u1e21\u1e24-\u1e27\u1e30-\u1e31\u1e40-\u1e41\u1e44-\u1e45\u1e56-\u1e57\u1e60-\u1e63\u1e6a-\u1e6b\u1e80-\u1e85\u1e8c-\u1e93\u1e9e\u1ea0-\u1ea7\u1eaa-\u1eac\u1eae-\u1ec1\u1ec4-\u1ed3\u1ed6-\u1edd\u1ee4-\u1ef9\u20ac])|(\u004d\u0302|\u004e\u0302|\u006d\u0302|\u006e\u0302|\u0044\u0302|\u0064\u0302|\u004a\u030c|\u004c\u0302|\u006c\u0302))*

注意:您不能将其复制粘贴到字符串文字中(例如"(([\u0009-\u000a...)",因为存在\u0009这样的字符。在编译之前,Java会将所有\uXXXX从源代码转换为它们代表的字符像

这样的代码
String str = "foo\u0009bar";

看起来就像是写的那样

String str = "foo
bar";

这不是有效的Java语言(字符串 literals 不能直接在其中包含行分隔符,而是用\n和/或{{ 1}})

例如,如果像\r这样的\u0009逃脱了,则可以将\传递给正则表达式引擎

\\u0009

现在让我们测试该正则表达式是否按预期工作:

String replacedRegex = "(([\\u0009-\\u000a\\u000d\\u0020-\\u007e\\u00a1-\\u00ac\\u00ae-\\u0107\\u010a-\\u011b\\u011e-\\u0123\\u0126-\\u0131\\u0134-\\u015b\\u015e-\\u016b\\u016e-\\u017e\\u018f\\u01a0-\\u01a1\\u01af-\\u01b0\\u01b7\\u01cd-\\u01d4\\u01de-\\u01df\\u01e4-\\u01f0\\u01f4-\\u01f5\\u01fa-\\u01ff\\u0218-\\u021b\\u021e-\\u021f\\u022a-\\u022b\\u022e-\\u0233\\u0259\\u0292\\u1e02-\\u1e03\\u1e0a-\\u1e0b\\u1e10-\\u1e11\\u1e1e-\\u1e21\\u1e24-\\u1e27\\u1e30-\\u1e31\\u1e40-\\u1e41\\u1e44-\\u1e45\\u1e56-\\u1e57\\u1e60-\\u1e63\\u1e6a-\\u1e6b\\u1e80-\\u1e85\\u1e8c-\\u1e93\\u1e9e\\u1ea0-\\u1ea7\\u1eaa-\\u1eac\\u1eae-\\u1ec1\\u1ec4-\\u1ed3\\u1ed6-\\u1edd\\u1ee4-\\u1ef9\\u20ac])|(\\u004d\\u0302|\\u004e\\u0302|\\u006d\\u0302|\\u006e\\u0302|\\u0044\\u0302|\\u0064\\u0302|\\u004a\\u030c|\\u004c\\u0302|\\u006c\\u0302))*";

输出:

Pattern RegExPruefung = Pattern.compile(replacedRegex);

String sut = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
System.out.println(RegExPruefung.matcher(sut).matches());
sut = "abc⺠";
System.out.println(RegExPruefung.matcher(sut).matches());