Java Regex匹配越南字符

我必须写一个正则表达式来限制输入字段,只允许越南字符,英语字符和数字。 我知道如何限制英语字符( [a-zA-Z] )和数字( [0-9] ),但不知道如何限制越南字符。

谁能给我一个java正则表达式匹配越南字符?

越南的字符就像:ể,ứ(编辑:但我不知道所有这些。否则,我可以使用[a-list-of-chars] ,或者可能有一个范围,如[ad]而不是[abcd]

越南字母

越南字母和英文字母的交集 (即2个字母之间的共同点)是英文字母减去fjwz

在越南语中, aeiouy被认为是元音。

除此之外,越南人还使用其他几个带有变音符号的字符。 下面列出了字符的大写字母(小写版本具有1个字符到1个字符的映射,与德语中的ß不同):

  • 辅音:

     Đ: LATIN CAPITAL LETTER D WITH STROKE 
  • 元音:

     Ă: LATIN CAPITAL LETTER A WITH BREVE Â: LATIN CAPITAL LETTER A WITH CIRCUMFLEX Ê: LATIN CAPITAL LETTER E WITH CIRCUMFLE Ô: LATIN CAPITAL LETTER O WITH CIRCUMFLEX Ơ: LATIN CAPITAL LETTER O WITH HORN Ư: LATIN CAPITAL LETTER U WITH HORN 

越南语有6个音调,除了第一个音调,其他5个音调由元音上的另一个变音符表示。 音调变音符号是锐,坟墓,钩 ,波浪形和dot以下的点。 由于有(6 + 6)个元音乘以5个带有变音符号的音调,加上6个已经在第一个音调上带有变音符号的元音,因此有66个带有变音符号的元音字形:

以下是所有(67)带有变音符号的辅音和元音的列表:

  Á À Ã Ả Ạ Ă Ắ Ằ Ẳ Ẵ Ặ Â Ấ Ầ Ẩ Ẫ Ậ Đ É È Ẻ Ẽ Ẹ Ê Ế Ề Ể Ễ Ệ Í Ì Ỉ Ĩ Ị Ô Ố Ồ Ổ Ỗ Ộ Ơ Ớ Ờ Ở Ỡ Ợ Ó Ò Õ Ỏ Ọ Ư Ứ Ừ Ử Ữ Ự Ú Ù Ủ Ũ Ụ Ý Ỳ Ỷ Ỹ Ỵ 

这些字符分布在Unicode中的不同拉丁块中。 我从字符图中手工挑选了这些字符,我必须小心不要选择与上面的字符在视觉上相同的字符。 当然,我们可以打印字符的名称,并检查它们是拉丁字符而不是希腊字母或西里尔字母。

 String VIETNAMESE_DIACRITIC_CHARACTERS = "ẮẰẲẴẶĂẤẦẨẪẬÂÁÀÃẢẠĐẾỀỂỄỆÊÉÈẺẼẸÍÌỈĨỊỐỒỔỖỘÔỚỜỞỠỢƠÓÒÕỎỌỨỪỬỮỰƯÚÙỦŨỤÝỲỶỸỴ"; for (char c: VIETNAMESE_DIACRITIC_CHARACTERS.toCharArray()) { System.out.println(c + ": " + Character.getName(c)); } 

结合性格

越南输入法如Unikey有两种模式:单一代码点模式(“Unicodedựngsẵn”)和组合标记模式(“Unicodetổhợp”)。

例如,对于相同的字符 (U + 1EE3),可以有几种方法来指定它:

  • 作为单个代码点(1个代码点):
  • 作为ơ (U + 01A1)和下面的组合点(U + 0323)(2个代码点)的组合: ợ
  • 作为o的组合,组合钩(U + 031B),并结合下面的点(U + 0323)(3个代码点): ợ

您可以将这些字符复制到浏览器的控制台中并检查其长度:

 ["ợ","ợ","ợ"].forEach(function (e) {console.log(e.length);}) 

如果要匹配上述所有这三种变体,则必须列出所有可能的组合和排列以指定字符, 并且必须对上面列出的所有具有变音符号的字符以及大写和小写都执行此操作。

够容易吗?

即使你回答是,你的代码也会成为无法理解的无法维护的混乱。

规范等价

由于有多种方法可以指定相同的文本 ,而不进行任何转换,因此无法将ợ相等。

 "ợ".equals("ợ") --> false 

因此,Unicode标准定义了所有3种方式,将上面的specify指定为规范等价 ,并定义了规范化字符串以进行比较的方法。

Java Pattern支持Canonical Equivalence

Pattern类的参考实现(由Oracle,在Windows和其他平台上广泛使用)具有(部分)支持使用Pattern.CANON_EQ模式的规范等价匹配。 从这个和这个错误报告中可以看出,它是无法使用的。 在撰写本文时,由于CANON_EQ被“支持”,因此所有版本都存在此错误,并且不太可能很快修复。 但是,它并没有完全破碎,我们仍然可以利用该选项目前提供的任何东西。

以下是匹配越南语+英语字母的Pattern的构造,:

 String VIETNAMESE_DIACRITIC_CHARACTERS = "ẮẰẲẴẶĂẤẦẨẪẬÂÁÀÃẢẠĐẾỀỂỄỆÊÉÈẺẼẸÍÌỈĨỊỐỒỔỖỘÔỚỜỞỠỢƠÓÒÕỎỌỨỪỬỮỰƯÚÙỦŨỤÝỲỶỸỴ"; Pattern p = Pattern.compile("(?:[" + VIETNAMESE_DIACRITIC_CHARACTERS + "]|[AZ])++", Pattern.CANON_EQ | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); 

附加标志Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE用于使模式匹配所有Unicode字符不区分大小写。 Pattern.CASE_INSENSITIVE仅使模式与US-ASCII字符集中的字符不区分大小写。

请注意, VIETNAMESE_DIACRITIC_CHARACTERS字符的顺序非常重要。 除非您理解其含义,否则我不建议更改字符的顺序。

在对其进行匹配之前,应使用Canonical Decomposition(NKD)或Canonical Composition(NKC)对输入进行标准化。 它确保组合标记符合规范。

无论输入是使用Canonical Composition还是Canonical Decomposition进行预处理,结果看起来都是一样的。 运行附录中的代码应该返回第二个和第三个输出的视觉上相同的结果:

Bạn chính là tác giả của Wikipedia Mọi người đều có thể biên tập bài ngay lập tức chỉ cần nhớ vài quy tắc Có sẵn rất nhiều trang trợ giúp như tạo bài sửa bài hay tải ảnh Bạn cũng đừng ngại đặt câu hỏi Hiện chúng ta có bài viết và thành viên

Bạn chính là tác giả của Wikipedia Mọi người đều có thể biên tập bài ngay lập tức chỉ cần nhớ vài quy tắc Có sẵn rất nhiều trang trợ giúp như tạo bài sửa bài hay tải ảnh Bạn cũng đừng ngại đặt câu hỏi Hiện chúng ta có bài viết và thành viên

尝试失败

以下是一些失败的尝试,这些尝试将用于解释为什么正则表达式的构造如上所示。

尝试1

 String VIETNAMESE_DIACRITIC_CHARACTERS = "ẮẰẲẴẶĂẤẦẨẪẬÂÁÀÃẢẠĐẾỀỂỄỆÊÉÈẺẼẸÍÌỈĨỊỐỒỔỖỘÔỚỜỞỠỢƠÓÒÕỎỌỨỪỬỮỰƯÚÙỦŨỤÝỲỶỸỴ"; Pattern p = Pattern.compile("[AZ" + VIETNAMESE_DIACRITIC_CHARACTERS + "]++", Pattern.CANON_EQ | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); 

为什么我们不将AZ包含在单个字符类中,而不是将其放在单独的字符类中并与变音字符类交替?

不,当我们尝试匹配输入字符串的规范分解时,结果会被破坏。 变音符号根本不匹配。

Ba n chi nh la ta c gia cu a Wikipedia Mo i ngu oi đe u co the bie n ta p ba i ngay la p tu c chi ca n nho va i quy ta c Co sa n ra t nhie u trang tro giu p nhu ta o ba i su a ba i hay ta ia nh Ba n cu ng đu ng nga i đa t ca u ho i Hie n chu ng ta co ba i vie t va tha nh vie n

尝试2

 String VIETNAMESE_DIACRITIC_CHARACTERS = "ÁÀÃẢẠĂẮẰẲẴẶÂẤẦẨẪẬĐÉÈẺẼẸÊẾỀỂỄỆÍÌỈĨỊÓÒÕỎỌÔỐỒỔỖỘƠỚỜỞỠỢÚÙỦŨỤƯỨỪỬỮỰÝỲỶỸỴ"; Pattern p = Pattern.compile("(?:[" + VIETNAMESE_DIACRITIC_CHARACTERS + "]|[AZ])++", Pattern.CANON_EQ | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); 

变音字符在字符类中声明,因此当我更改字符的顺序时代码应该表现相同……对吗?

不,当我们尝试匹配输入字符串的规范分解时,一些结果会被破坏。

Bạn chính là tác giả của Wikipedia Mọi ngươ i đê u có thê biên tạ p bài ngay lạ p tư c chỉ câ n nhơ vài quy tă c Có să n râ t nhiê u trang trơ giúp như tạo bài sư a bài hay tải ảnh Bạn cũng đư ng ngại đạ t câu hỏi Hiẹ n chúng ta có bài viê t và thành viên

说明

参考实现(Oracle)通过挑选表达式中的字符来实现Pattern.CANON_EQ模式,该字符可以在Canonical Decomposition下扩展为多个字符,并执行正则表达式的文本转换。 然后,表达式将按照正常情况进行编译。

转换正则表达式的第一个过程不能正确解析表达式,因此它表现出非常简单匹配的疯狂行为,如上面的错误报告中所示。

幸运的是,如果存在不匹配的(在正则表达式中), Pattern类会在转换后吐出正则表达式。因此,我们可以添加(最后触发PatternSyntaxException并查看已转换的正则表达式字符串。

让我们弄乱上面的解决方案正则表达式,看看进入编译步骤的正则表达式字符串是什么:

 java.util.regex.PatternSyntaxException: Unclosed group near index 596 (?:(?:[Đ]|Ắ|Ắ|Ắ|Ằ|Ằ|Ằ|Ẳ|Ẳ|Ẳ|Ẵ|Ẵ|Ẵ|Ặ|Ặ|Ặ|Ặ|Ặ|Ặ|Ă|Ă|Ấ|Ấ|Ấ|Ầ|Ầ|Ầ|Ẩ|Ẩ|Ẩ|Ẫ|Ẫ|Ẫ|Ậ|Ậ|Ậ|Ậ|Ậ|Ậ|Â|Â|Á|Á|À|À|Ã|Ã|Ả|Ả|Ạ|Ạ|Ế|Ế|Ế|Ề|Ề|Ề|Ể|Ể|Ể|Ễ|Ễ|Ễ|Ệ|Ệ|Ệ|Ệ|Ệ|Ệ|Ê|Ê|É|É|È|È|Ẻ|Ẻ|Ẽ|Ẽ|Ẹ|Ẹ|Í|Í|Ì|Ì|Ỉ|Ỉ|Ĩ|Ĩ|Ị|Ị|Ố|Ố|Ố|Ồ|Ồ|Ồ|Ổ|Ổ|Ổ|Ỗ|Ỗ|Ỗ|Ộ|Ộ|Ộ|Ộ|Ộ|Ộ|Ô|Ô|Ớ|Ớ|Ớ|Ớ|Ớ|Ớ|Ờ|Ờ|Ờ|Ờ|Ờ|Ờ|Ở|Ở|Ở|Ở|Ở|Ở|Ỡ|Ỡ|Ỡ|Ỡ|Ỡ|Ỡ|Ợ|Ợ|Ợ|Ợ|Ợ|Ợ|Ơ|Ơ|Ó|Ó|Ò|Ò|Õ|Õ|Ỏ|Ỏ|Ọ|Ọ|Ứ|Ứ|Ứ|Ứ|Ứ|Ứ|Ừ|Ừ|Ừ|Ừ|Ừ|Ừ|Ử|Ử|Ử|Ử|Ử|Ử|Ữ|Ữ|Ữ|Ữ|Ữ|Ữ|Ự|Ự|Ự|Ự|Ự|Ự|Ư|Ư|Ú|Ú|Ù|Ù|Ủ|Ủ|Ũ|Ũ|Ụ|Ụ|Ý|Ý|Ỳ|Ỳ|Ỷ|Ỷ|Ỹ|Ỹ|Ỵ|Ỵ)|[AZ])++( ^ 

我们可以看到,引擎抓取所有可以在Canonical Decomposition下扩展的字符,将它带到字符类之外并构建一个替换。

对于在交替中重复的相同字符发生了什么仍然不是很清楚,所以我将在每个字符之间插入空格:

( ? : ( ? : [ Đ ] | A ̆ ́ | Ă ́ | Ắ | A ̆ ̀ | Ă ̀ | Ằ | A ̆ ̉ | Ă ̉ | Ẳ | A ̆ ̃ | Ă ̃ | Ẵ | A ̣ ̆ | Ạ ̆ | Ặ | A ̆ ̣ | Ă ̣ | Ặ | A ̆ | Ă | A ̂ ́ | Â ́ | Ấ | A ̂ ̀ | Â ̀ | Ầ | A ̂ ̉ | Â ̉ | Ẩ | A ̂ ̃ | Â ̃ | Ẫ | A ̣ ̂ | Ạ ̂ | Ậ | A ̂ ̣ | Â ̣ | Ậ | A ̂ | Â | A ́ | Á | A ̀ | À | A ̃ | Ã | A ̉ | Ả | A ̣ | Ạ | E ̂ ́ | Ê ́ | Ế | E ̂ ̀ | Ê ̀ | Ề | E ̂ ̉ | Ê ̉ | Ể | E ̂ ̃ | Ê ̃ | Ễ | E ̣ ̂ | Ẹ ̂ | Ệ | E ̂ ̣ | Ê ̣ | Ệ | E ̂ | Ê | E ́ | É | E ̀ | È | E ̉ | Ẻ | E ̃ | Ẽ | E ̣ | Ẹ | I ́ | Í | I ̀ | Ì | I ̉ | Ỉ | I ̃ | Ĩ | I ̣ | Ị | O ̂ ́ | Ô ́ | Ố | O ̂ ̀ | Ô ̀ | Ồ | O ̂ ̉ | Ô ̉ | Ổ | O ̂ ̃ | Ô ̃ | Ỗ | O ̣ ̂ | Ọ ̂ | Ộ | O ̂ ̣ | Ô ̣ | Ộ | O ̂ | Ô | O ̛ ́ | Ơ ́ | Ớ | O ́ ̛ | Ó ̛ | Ớ | O ̛ ̀ | Ơ ̀ | Ờ | O ̀ ̛ | Ò ̛ | Ờ | O ̛ ̉ | Ơ ̉ | Ở | O ̉ ̛ | Ỏ ̛ | Ở | O ̛ ̃ | Ơ ̃ | Ỡ | O ̃ ̛ | Õ ̛ | Ỡ | O ̛ ̣ | Ơ ̣ | Ợ | O ̣ ̛ | Ọ ̛ | Ợ | O ̛ | Ơ | O ́ | Ó | O ̀ | Ò | O ̃ | Õ | O ̉ | Ỏ | O ̣ | Ọ | U ̛ ́ | Ư ́ | Ứ | U ́ ̛ | Ú ̛ | Ứ | U ̛ ̀ | Ư ̀ | Ừ | U ̀ ̛ | Ù ̛ | Ừ | U ̛ ̉ | Ư ̉ | Ử | U ̉ ̛ | Ủ ̛ | Ử | U ̛ ̃ | Ư ̃ | Ữ | U ̃ ̛ | Ũ ̛ | Ữ | U ̛ ̣ | Ư ̣ | Ự | U ̣ ̛ | Ụ ̛ | Ự | U ̛ | Ư | U ́ | Ú | U ̀ | Ù | U ̉ | Ủ | U ̃ | Ũ | U ̣ | Ụ | Y ́ | Ý | Y ̀ | Ỳ | Y ̉ | Ỷ | Y ̃ | Ỹ | Y ̣ | Ỵ ) | [ A - Z ] ) + + (

我们可以看到同一个字符重复的串并不是真的相同 – 它们是表示相同字符的不同序列。

使用相同的方法,让我们分析尝试2中的正则表达式,看看它失败的原因。

 java.util.regex.PatternSyntaxException: Unclosed group near index 596 (?:(?:[Đ]|Á|Á|À|À|Ã|Ã|Ả|Ả|Ạ|Ạ|Ă|Ă|Ắ|Ắ|Ắ|Ằ|Ằ|Ằ|Ẳ|Ẳ|Ẳ|Ẵ|Ẵ|Ẵ|Ặ|Ặ|Ặ|Ặ|Ặ|Ặ|Â|Â|Ấ|Ấ|Ấ|Ầ|Ầ|Ầ|Ẩ|Ẩ|Ẩ|Ẫ|Ẫ|Ẫ|Ậ|Ậ|Ậ|Ậ|Ậ|Ậ|É|É|È|È|Ẻ|Ẻ|Ẽ|Ẽ|Ẹ|Ẹ|Ê|Ê|Ế|Ế|Ế|Ề|Ề|Ề|Ể|Ể|Ể|Ễ|Ễ|Ễ|Ệ|Ệ|Ệ|Ệ|Ệ|Ệ|Í|Í|Ì|Ì|Ỉ|Ỉ|Ĩ|Ĩ|Ị|Ị|Ó|Ó|Ò|Ò|Õ|Õ|Ỏ|Ỏ|Ọ|Ọ|Ô|Ô|Ố|Ố|Ố|Ồ|Ồ|Ồ|Ổ|Ổ|Ổ|Ỗ|Ỗ|Ỗ|Ộ|Ộ|Ộ|Ộ|Ộ|Ộ|Ơ|Ơ|Ớ|Ớ|Ớ|Ớ|Ớ|Ớ|Ờ|Ờ|Ờ|Ờ|Ờ|Ờ|Ở|Ở|Ở|Ở|Ở|Ở|Ỡ|Ỡ|Ỡ|Ỡ|Ỡ|Ỡ|Ợ|Ợ|Ợ|Ợ|Ợ|Ợ|Ú|Ú|Ù|Ù|Ủ|Ủ|Ũ|Ũ|Ụ|Ụ|Ư|Ư|Ứ|Ứ|Ứ|Ứ|Ứ|Ứ|Ừ|Ừ|Ừ|Ừ|Ừ|Ừ|Ử|Ử|Ử|Ử|Ử|Ử|Ữ|Ữ|Ữ|Ữ|Ữ|Ữ|Ự|Ự|Ự|Ự|Ự|Ự|Ý|Ý|Ỳ|Ỳ|Ỷ|Ỷ|Ỹ|Ỹ|Ỵ|Ỵ)|[AZ])++( ^ 

在每个字符之间插入空格:

( ? : ( ? : [ Đ ] | A ́ | Á | A ̀ | À | A ̃ | Ã | A ̉ | Ả | A ̣ | Ạ | A ̆ | Ă | A ̆ ́ | Ă ́ | Ắ | A ̆ ̀ | Ă ̀ | Ằ | A ̆ ̉ | Ă ̉ | Ẳ | A ̆ ̃ | Ă ̃ | Ẵ | A ̣ ̆ | Ạ ̆ | Ặ | A ̆ ̣ | Ă ̣ | Ặ | A ̂ | Â | A ̂ ́ | Â ́ | Ấ | A ̂ ̀ | Â ̀ | Ầ | A ̂ ̉ | Â ̉ | Ẩ | A ̂ ̃ | Â ̃ | Ẫ | A ̣ ̂ | Ạ ̂ | Ậ | A ̂ ̣ | Â ̣ | Ậ | E ́ | É | E ̀ | È | E ̉ | Ẻ | E ̃ | Ẽ | E ̣ | Ẹ | E ̂ | Ê | E ̂ ́ | Ê ́ | Ế | E ̂ ̀ | Ê ̀ | Ề | E ̂ ̉ | Ê ̉ | Ể | E ̂ ̃ | Ê ̃ | Ễ | E ̣ ̂ | Ẹ ̂ | Ệ | E ̂ ̣ | Ê ̣ | Ệ | I ́ | Í | I ̀ | Ì | I ̉ | Ỉ | I ̃ | Ĩ | I ̣ | Ị | O ́ | Ó | O ̀ | Ò | O ̃ | Õ | O ̉ | Ỏ | O ̣ | Ọ | O ̂ | Ô | O ̂ ́ | Ô ́ | Ố | O ̂ ̀ | Ô ̀ | Ồ | O ̂ ̉ | Ô ̉ | Ổ | O ̂ ̃ | Ô ̃ | Ỗ | O ̣ ̂ | Ọ ̂ | Ộ | O ̂ ̣ | Ô ̣ | Ộ | O ̛ | Ơ | O ̛ ́ | Ơ ́ | Ớ | O ́ ̛ | Ó ̛ | Ớ | O ̛ ̀ | Ơ ̀ | Ờ | O ̀ ̛ | Ò ̛ | Ờ | O ̛ ̉ | Ơ ̉ | Ở | O ̉ ̛ | Ỏ ̛ | Ở | O ̛ ̃ | Ơ ̃ | Ỡ | O ̃ ̛ | Õ ̛ | Ỡ | O ̛ ̣ | Ơ ̣ | Ợ | O ̣ ̛ | Ọ ̛ | Ợ | U ́ | Ú | U ̀ | Ù | U ̉ | Ủ | U ̃ | Ũ | U ̣ | Ụ | U ̛ | Ư | U ̛ ́ | Ư ́ | Ứ | U ́ ̛ | Ú ̛ | Ứ | U ̛ ̀ | Ư ̀ | Ừ | U ̀ ̛ | Ù ̛ | Ừ | U ̛ ̉ | Ư ̉ | Ử | U ̉ ̛ | Ủ ̛ | Ử | U ̛ ̃ | Ư ̃ | Ữ | U ̃ ̛ | Ũ ̛ | Ữ | U ̛ ̣ | Ư ̣ | Ự | U ̣ ̛ | Ụ ̛ | Ự | Y ́ | Ý | Y ̀ | Ỳ | Y ̉ | Ỷ | Y ̃ | Ỹ | Y ̣ | Ỵ ) | [ A - Z ] ) + + (

注意A ̂ | Â A ̂ | Â来自A ̂ ̀ | Â ̀ | Ầ A ̂ ̀ | Â ̀ | Ầ A ̂ ̀ | Â ̀ | Ầ在正则表达式中。 这意味着将首先在输入ẦA ̂ ̀上尝试A ̂ ̀ ,并且当它在下一次迭代中无法匹配时,重复将结束。

由于交替的顺序很重要,作为一般规则,在一个字符串是另一个字符串的前缀的2个字符串之间,较长的字符串应该在交替中首先出现。 在我们的例子中,我们需要在角色之前放置具有更多变音符号的字符,而使用较少或没有变音符号。

尝试1的问题相同:

 java.util.regex.PatternSyntaxException: Unclosed group near index 589 (?:[A-ZĐ]|Ắ|Ắ|Ắ|Ằ|Ằ|Ằ|Ẳ|Ẳ|Ẳ|Ẵ|Ẵ|Ẵ|Ặ|Ặ|Ặ|Ặ|Ặ|Ặ|Ă|Ă|Ấ|Ấ|Ấ|Ầ|Ầ|Ầ|Ẩ|Ẩ|Ẩ|Ẫ|Ẫ|Ẫ|Ậ|Ậ|Ậ|Ậ|Ậ|Ậ|Â|Â|Á|Á|À|À|Ã|Ã|Ả|Ả|Ạ|Ạ|Ế|Ế|Ế|Ề|Ề|Ề|Ể|Ể|Ể|Ễ|Ễ|Ễ|Ệ|Ệ|Ệ|Ệ|Ệ|Ệ|Ê|Ê|É|É|È|È|Ẻ|Ẻ|Ẽ|Ẽ|Ẹ|Ẹ|Í|Í|Ì|Ì|Ỉ|Ỉ|Ĩ|Ĩ|Ị|Ị|Ố|Ố|Ố|Ồ|Ồ|Ồ|Ổ|Ổ|Ổ|Ỗ|Ỗ|Ỗ|Ộ|Ộ|Ộ|Ộ|Ộ|Ộ|Ô|Ô|Ớ|Ớ|Ớ|Ớ|Ớ|Ớ|Ờ|Ờ|Ờ|Ờ|Ờ|Ờ|Ở|Ở|Ở|Ở|Ở|Ở|Ỡ|Ỡ|Ỡ|Ỡ|Ỡ|Ỡ|Ợ|Ợ|Ợ|Ợ|Ợ|Ợ|Ơ|Ơ|Ó|Ó|Ò|Ò|Õ|Õ|Ỏ|Ỏ|Ọ|Ọ|Ứ|Ứ|Ứ|Ứ|Ứ|Ứ|Ừ|Ừ|Ừ|Ừ|Ừ|Ừ|Ử|Ử|Ử|Ử|Ử|Ử|Ữ|Ữ|Ữ|Ữ|Ữ|Ữ|Ự|Ự|Ự|Ự|Ự|Ự|Ư|Ư|Ú|Ú|Ù|Ù|Ủ|Ủ|Ũ|Ũ|Ụ|Ụ|Ý|Ý|Ỳ|Ỳ|Ỷ|Ỷ|Ỹ|Ỹ|Ỵ|Ỵ)++( ^ 

由于在原始字符类之后形成交替,因此将首先尝试[AZ]的元音,导致重复在遇到杂散组合标记时提前终止。

参考

  • 越南字母系统
  • 常见的越南语输入法
  • 优化的Unicode组合和分解

附录

以下是测试程序的源代码。

在ideone上演示

 import java.util.regex.*; import java.text.*; class Ideone { public static void main (String[] args) throws java.lang.Exception { String VIETNAMESE_DIACRITIC_CHARACTERS = "ẮẰẲẴẶĂẤẦẨẪẬÂÁÀÃẢẠĐẾỀỂỄỆÊÉÈẺẼẸÍÌỈĨỊỐỒỔỖỘÔỚỜỞỠỢƠÓÒÕỎỌỨỪỬỮỰƯÚÙỦŨỤÝỲỶỸỴ"; /* for (char c: VIETNAMESE_DIACRITIC_CHARACTERS.toCharArray()) { System.out.println(c + ": " + Character.getName(c)); } */ String tests[] = new String[3]; tests[0] = "Bạn chính là tác giả của Wikipedia!\n" + "Mọi người đều có thể biên tập bài ngay lập tức, chỉ cần nhớ vài quy tắc." + "Có sẵn rất nhiều trang trợ giúp như tạo bài, sửa bài hay tải ảnh." + "Bạn cũng đừng ngại đặt câu hỏi.\n" + "Hiện chúng ta có 1.109.446 bài viết và 406.782 thành viên."; tests[1] = Normalizer.normalize(tests[0], Normalizer.Form.NFD); /* for (char c: tests[1].toCharArray()) { System.out.printf("%04x ", (int) c); } */ tests[2] = Normalizer.normalize(tests[0], Normalizer.Form.NFC); try { Pattern p = Pattern.compile("(?:[" + VIETNAMESE_DIACRITIC_CHARACTERS + "]|[AZ])++", Pattern.CANON_EQ | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); for (String t: tests) { Matcher m = p.matcher(t); while (m.find()) { System.out.print(m.group() + " "); } System.out.println(); } } catch (Exception e) { System.out.println(e); } } } 

正则表达式不关心其越南语,丹麦语等。

 System.out.println("Biểuthức".matches("\\D+")); // true 

编辑:

 // You have to add every Vietnamese chars in the below regex: System.out.println("Biểuthức".matches("[\\w ểứ]*")); // true