现在,从您编辑的示例中,我可以看到您想要的内容。您也对此表示同情。Java的正则表达式距离您在Ruby或Perl中发现的便利还有很长的路要走。而且它们几乎永远都是;这个问题无法解决,所以我们永远都陷入这种混乱之中-至少在Java中是如此。其他JVM语言在此方面做得更好,尤其是Groovy。但是他们仍然遭受一些固有的缺陷,并且只能走得很远。
从哪里开始?有String类的所谓的便利方法:matches,replaceAll,replaceFirst,和split。在小型程序中,有时这些可以没问题,这取决于您如何使用它们。但是,它们确实确实存在一些问题,看来您已经发现了。以下是这些问题的部分列表,以及有关这些问题可以做和不能做的事情。
不便的方法非常奇怪地称为“ matches”,但是它要求您在正则表达式的两边都加注以匹配整个字符串。这种违反直觉的感觉与任何以前的语言中使用的单词match的任何感觉都相反,并且不断地咬人。传递到其他三种不便方法中的模式的工作方式与此非常不同,因为在其他三种方法中,它们的工作方式与普通模式在其他任何地方都一样。只是不在matches。这意味着您不能只是复制您的模式,即使是出于良善而在同一织补类中的方法中!而且,没有find便捷的方法可以完成世界上其他匹配器的工作。该matches方法应该被称为FullMatch,并且应该有一个PartialMatch或find 方法添加到String类。
没有API允许您将Pattern.compile标志以及用于String类的4个与模式相关的便捷方法的字符串一起传递。这意味着您必须依赖像(?i)和这样的字符串版本(?x),但并非所有可能的Pattern编译标志都存在这些字符串版本。至少可以说这是非常不方便的。
split在某些情况下,该方法不会返回与splitJava借用的语言所返回的结果相同的结果。这是一个偷偷摸摸的小陷阱。如果拆分空字符串,您认为应该返回多少个元素,是吗?Java制造商在应该有一个假冒的return元素的地方,这意味着您无法区分合法结果和虚假结果。在上分裂是一个严重的设计缺陷”:”,您无法分辨”“vs的输入之间的差异。”:”。真是的 人们没有测试过这些东西吗?再有,破碎的和根本上不可靠的行为是无法修复的:您绝不能改变事物,甚至是破碎的事物。用Java破坏其他地方的东西是不行的。破碎永远在这里。
正则表达式的反斜杠表示法与字符串中使用的反斜杠表示法冲突。这使它变得超级笨拙,并且也容易出错,因为您必须不断在所有内容上添加反斜杠,并且很容易忘记一个斜杠并且既没有警告也没有成功。诸如bw+b印刷之类的简单模式已成为噩梦:”bw+b”。祝你读完那本书好运。有些人在其模式上使用斜杠反相器功能,以便可以将其写为”/b/w+/b”代替。除了从字符串中读取模式之外,没有其他方法可以以所见即所得的字面方式构造模式。它总是充满反斜杠。您是否在正确的地方都得到了它们,足够了吗?如果是这样,那么它真的很难阅读。如果不是,您可能还没有全部了解。至少像Groovy这样的JVM语言在这里已经找到了正确的答案:为人们提供一类的正则表达式,因此您不会发疯。这是Groovy regex示例的公平集合,显示了它可以而且应该多么简单。
该(?x)模式存在严重缺陷。它不需要Java风格的注释,// COMMENT而需要Shell风格的注释# COMMENT。它不适用于多行字符串。它不接受文字作为文字,这会导致上面列出的反斜杠问题,这从根本上损害了任何整理内容的尝试,例如使所有注释都在同一列上开始。由于存在反斜杠,您可以使它们在源代码字符串的同一列上开始,并在打印时将其拧紧,反之亦然。这么多的可读性!
在正则表达式中输入Unipre字符非常困难,而且实际上根本上是无法解决的。有像象征性人物命名不支持N{QUOTATION MARK},N{LATIN SMALL LETTER E WITH GRAVE}或N{MATHEMATICAL BOLD CAPITAL C}。这意味着您陷入了难以维护的魔术数字之中。而且,您甚至都无法按代码点输入它们。您不能u0022用于第一个,因为Java预处理器使该语法错误。因此,您u0022转而转到,直到下一个u00E8都不能用这种方式输入,否则它将起作用,直到无法进入下一个,否则它将破坏CANON_EQ标志。最后一个纯粹是一场噩梦:它的代码点是U + 1D402,但是Java不支持使用正则表达式中它们的代码点号来支持完整的Unipre集,这迫使您弄清计算器以找出那是uD835uDC02或uD835uDC02(但不是uD835uDC02),非常疯狂。但是由于设计错误,您不能在字符类中使用这些字符,从而使说不准匹配,[N{MATHEMATICAL BOLD CAPITAL A}-N{MATHEMATICAL BOLD CAPITAL Z}]因为正则表达式编译器将UTF-16搞砸了。同样,这永远无法解决,否则它将更改旧程序。通过使用编译Java来解决Java的Unipre-in-source-pre的问题,您甚至无法解决该错误java -encoding UTF-8,因为愚蠢的事情将字符串存储为讨厌的UTF-16,这必定会在字符类中将其破坏。 糟糕!
Java缺少了许多其他语言要依赖的正则表达式。没有命名组的示例,甚至没有相对编号的组。这使得从较小的模式构造较大的模式从根本上容易出错。有一个前端库,使您可以使用简单的命名组,实际上,这最终会在生产JDK7中出现。但是即使如此,也没有任何机制可以处理多个同名小组。而且,您仍然没有相对编号的缓冲区。我们又回到了糟糕的过去,这些东西早已解决了。
不支持换行符序列,它是标准中仅有的两个“强烈推荐”的部分之一,建议将R其用于此目的。由于其可变长度的性质以及Java对字素的支持,这很难模仿。
字符类转义不适用于Java的本机字符集!是的,这是正确的:诸如wand s(或更确切地说,”w”and “b”)之类的常规内容不适用于Java中的Unipre!这不是很酷的复古。更糟糕的是,Java的b(使that”b”与不相同”b”)确实具有一些Unipre敏感性,尽管不是标准所要求的。因此,例如像绳子”élève”从来没有在Java将匹配的模式bw+b,而不是仅仅在每全部Pattern.matches,但确实是在没有任何一点你可能从得到Pattern.find。这简直就是乞be信仰。他们破坏了w和之间的固有联系b,然后误定义了它们来引导!它甚至不知道什么是Unipre字母代码点。这是最严重的问题,他们永远无法修复,因为这会改变现有代码的行为,这在Java Universe中是严格禁止的。最好的办法是创建一个重写库,该库在进入编译阶段之前就充当前端。这样,您就可以将模式从1960年代强制迁移到21世纪的文本处理。
支持的唯一两个Unipre属性是“常规类别”和“块”属性。一般的类别属性只支持类的缩写p{Sk},违背了标准的强烈推荐中,也允许p{Modifier Symbol},p{Modifier_Symbol}等你甚至不得到所需要的别名标准说,你应该。这使您的代码更加难以理解和难以维护。您最终将获得对生产JDK7中的script属性的支持,但这仍然严重不足标准规定的11个基本属性集,即使是最低级别的Unipre支持,该11个基本属性也必须提供。
Java确实提供的一些微不足道的属性是人造amis:它们具有与正式Unipre属性名称相同的名称,但它们的作用完全不同。例如,Unipre要求p{alpha}与Unipre相同p{Alphabetic},但是Java仅使其成为过时的且不再古雅的7位字母,这超过4个数量级,太少了。空格是另一个缺陷,因为您使用伪装为Unipre空格的Java版本,因此您的UTF-8解析器将因为其NO-BREAK SPACE代码点而中断,Unipre通常将其视为空格,但是Java忽略了这一要求,因此中断了您的解析器。
通常不提供对字素的支持X。这使得无数您需要和想要对正则表达式执行的常见任务成为不可能。由于Java几乎不支持Unipre属性,因此扩展的字形簇不仅触手可及,甚至无法使用standard来近似旧的传统字形簇(?:p{Grapheme_base}p{Grapheme_Extend}]*)。无法使用字素使得即使最简单的Unipre文本处理也无法实现。例如,无论Java中的变音符号如何,都无法匹配元音。您在带有字素支持的语言中执行此操作的方式各不相同,但至少您应该能够将其放入NFD中并进行匹配(?:(?=[aeiou])X)。在Java中,您甚至不能做那么多:字素超出了您的范围。这意味着Java甚至无法处理自己的本机字符集。它为您提供Unipre,然后使其无法使用。
String类中的便捷方法不缓存已编译的正则表达式。实际上,不存在诸如在编译时进行语法检查的编译时模式之类的东西,即应该在语法检查时进行。这意味着您的程序只使用在编译时完全理解的恒定正则表达式,如果您忘记了此处的反斜杠,或者由于前面讨论的缺陷而不会做反斜杠,那么它将在运行中发生异常炸弹。甚至Groovy都能正确地完成这一部分。正则表达式的结构太高级了,无法用Java的令人讨厌的事后螺栓连接模型处理-对常规文本处理来说,它们太重要了,不容忽视。Java对于这种语言来说太底层了,它无法提供简单的机制来使您自己构建所需的东西:您无法从这里得到实现。
在String和Pattern类被标记final在Java中。这完全消除了使用适当的OO设计扩展这些类的任何可能性。您不能通过matches子类化和替换来创建更好的方法版本。哎呀,你甚至不能继承!最终不是解决方案;最后的是死刑判决,不得上诉。
最后,为了向您展示大脑受损的Java正则表达式的真实程度,请考虑这种多行模式,该模式显示了许多已经描述的缺陷:
String rx =
“(?= ^ p{Lu} [_pLpMd-] + $)n”
+ ” # next is a big can’t-have set n”
+ “(?! ^ .* n”
+ ” (?: ^ d+ $ n”
+ ” | ^ p{Lu} - p{Lu} $ n”
+ ” | Invitrogen n”
+ ” | Clontech n”
+ ” | L-L-X-X # dashes ok n”
+ ” | Sarstedt n”
+ ” | Roche n”
+ ” | Beckmann”
+ ” | Bayer n”
+ ” ) # end alternatives n”
+ ” b # only on a word boundary n”
+ “) # end negated lookahead n”
;
你看到那是多么不自然吗?您必须在字符串中添加文字换行符;您必须使用非Java注释;由于多余的反斜杠,您无法排列任何内容;您必须使用在Unipre上无法正常使用的事物的定义。除此之外,还有许多其他问题。
不仅没有解决这些严重缺陷的计划,而且几乎不可能修复几乎所有这些缺陷,因为您要更改旧程序。即使是普通的OO设计工具也被禁止使用,因为它们都被死刑判决的终结所锁定,并且无法修复。
所以阿里努里,如果你觉得Java的正则表达式笨拙过于大清洗可靠,便捷的正则表达式处理永远是在Java中有可能,我不能反驳你。抱歉,这就是事实。
“已在下一版本中修复!”
仅仅因为某些事情永远无法解决并不意味着什么都无法解决。只是必须非常小心地完成。我所知道的东西已经在当前的JDK7或建议的JDK8构建中得到修复:
现在支持Unipre脚本属性。您可以使用任何的等价形式
p{script=Greek},p{sc=Greek},p{IsGreek},或p{Greek}。这在本质上优于旧的笨重块属性。这意味着您可以执行[p{Latin}p{Common}p{Inherited}],这很重要。UTF-16错误具有解决方法。现在,您可以使用
x{⋯}符号通过编号指定任何Unipre
代码点x{1D402}。即使在字符类中也可以使用,最终可以[x{1D400}-x{1D419}]正常工作。不过,您仍然必须加倍反斜杠,并且它只能在regexex中工作,而不是通常应使用的字符串。现在,通过标准符号支持命名组
(?<NAME>⋯)
来创建和k<NAME>
反向引用它。这些也仍然有助于数字组号。但是,您不能以相同的模式获得多个其中的一个,也不能将它们用于递归。一种新的模式编译标志,
Pattern.UNICODE_CHARACTER_CLASSES
以及相关的嵌入式交换机(?U),现在会掉周围的一切事物一样的定义w,b,p{alpha},和p{punct},使他们现在符合的Unipre标准所要求的那些东西的定义。现在支持缺少或定义不正确的二进制属性
p{IsLowercase},p{IsUppercase}和p{IsAlphabetic},它们对应于Character类中的方法。这很重要,因为Unipre在纯字母与大小写或字母代码点之间做出了重要而普遍的区分。这些关键属性是11类必需的,这些11个必需属性是符合UTS#18的1级标准“ Unipre Regular Expresions”所绝对必需的,否则,您实际上无法使用Unipre。
这些增强和修复对最终拥有非常重要,因此,我很高兴,甚至很高兴拥有它们。
但是对于工业强度,最新的正则表达式和/或Unipre工作,我不会使用Java。如果您敢于使用Java提供的字符集,那么Java的20年后仍不完善的Unipre模型中缺少太多内容,无法完成实际工作。而且,螺栓连接式模型永远无法使用,而所有Java regexe都是如此。您必须从Groovy的工作原理重新开始。
当然,它可能适用于非常有限的应用程序,这些应用程序的小客户群仅限于爱荷华州农村地区的英语母语群体,无需外部交互,也无需使用老式电报可以发送的字符。但对于有多少项目是这是真的吗?事实证明,您所想的甚至更少。
正是由于这个原因,最近才取消了某个(而且很明显)数十亿美元的重要应用程序的国际部署。Java的Unipre支持(不仅在正则表达式中,而且在整个过程中)都太弱了,以至于无法在Java中可靠地完成所需的国际化。因此,他们被迫从最初计划的全球范围部署缩减为仅在美国部署。肯定是狭och的。不,有NᴏᴛHᴀᴘᴘʏ;你会吗
Java已经有20年的时间使它正确,而且显然到目前为止他们还没有做到,所以我不会屏息。或把好钱丢在坏处;这里的教训是忽略炒作,而是适用尽职调查做出非常确保所有必要的基础设施支持,是有之前你投入太多。否则,一旦您无法解决项目,也可能会陷入没有任何实际选择的困境。



