当前位置: 首页 > 面试题库 >

如何添加Java正则表达式实现中缺少的功能?

史默
2023-03-14
问题内容

我是Java新手。作为.Net开发人员,我非常习惯Regex.Net中的类。Regex(正则表达式)的Java实现不错,但是缺少一些关键功能。

我想为Java创建自己的帮助程序类,但我想也许已经有一个可用的类了。那么,有没有免费的,易于使用的Java正则表达式产品可用,还是我应该自己创建一个?

如果我要编写自己的课程,您认为我应该在哪里共享该课程,以供其他人使用?

[编辑]

有人抱怨我没有解决当前Regex班级的问题。我将尝试澄清我的问题。

在.Net中,使用正则表达式比在Java中更容易。由于两种语言都是面向对象的,并且在许多方面都非常相似,因此我希望在两种语言中使用正则表达式都具有类似的经验。不幸的是,事实并非如此。

这是在Java和C#中进行比较的一些代码。第一个是C#,第二个是Java:

在C#中:

string source = "The colour of my bag matches the color of my shirt!";
string pattern = "colou?r";

foreach(Match match in Regex.Matches(source, pattern))
{
    Console.WriteLine(match.Value);
}

在Java中:

String source = "The colour of my bag matches the color of my shirt!";
String pattern = "colou?r";
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(source);

while(m.find())
{
    System.out.println(source.substring(m.start(), m.end()));
}

在上面的示例代码中,我试图对两种语言公平。

您在这里注意到的第一件事是该类的.Value成员Match(与在Java中使用.start()和相比.end())。

当我可以调用诸如Regex.MatchesRegex.Match等静态函数时,为什么要创建两个对象?

在更高级的用法中,差异显示了更多。看方法Groups,辞典长度Capture,Index,Length,Success,等等,这些都是非常必要的功能,在我看来,应该为Java提供了。

当然,所有这些功能都可以通过自定义代理(帮助程序)类手动添加。这是我问这个问题的主要原因。Regex在Perl中,我们没有轻而易举的事,但是至少我们可以使用.Net方法Regex,我认为它是非常巧妙的设计。


问题答案:

现在,从您编辑的示例中,我可以看到您想要的内容。您也对此表示同情。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破坏其他地方的东西是不行的。破碎永远在这里。

正则表达式的反斜杠表示法与字符串中使用的反斜杠表示法冲突。这使它变得超级笨拙,并且也容易出错,因为您必须不断在所有内容上添加反斜杠,并且很容易忘记一个斜杠并且既没有警告也没有成功。诸如\b\w+\b印刷之类的简单模式已成为噩梦:”\b\w+\b”。祝你读完那本书好运。有些人在其模式上使用斜杠反相器功能,以便可以将其写为”/b/w+/b”代替。除了从字符串中读取模式之外,没有其他方法可以以所见即所得的字面方式构造模式。它总是充满反斜杠。您是否在正确的地方都得到了它们,足够了吗?如果是这样,那么它真的很难阅读。如果不是,您可能还没有全部了解。至少像Groovy这样的JVM语言在这里已经找到了正确的答案:为人们提供一类的正则表达式,因此您不会发疯。这是Groovy regex示例的公平集合,显示了它可以而且应该多么简单。

该(?x)模式存在严重缺陷。它不需要Java风格的注释,// COMMENT而需要Shell风格的注释# COMMENT。它不适用于多行字符串。它不接受文字作为文字,这会导致上面列出的反斜杠问题,这从根本上损害了任何整理内容的尝试,例如使所有注释都在同一列上开始。由于存在反斜杠,您可以使它们在源代码字符串的同一列上开始,并在打印时将其拧紧,反之亦然。这么多的可读性!

在正则表达式中输入Unicode字符非常困难,而且实际上根本上是无法解决的。有像象征性人物命名不支持\N{QUOTATION MARK},\N{LATIN SMALL LETTER E WITH GRAVE}或\N{MATHEMATICAL BOLD CAPITAL C}。这意味着您陷入了难以维护的魔术数字之中。而且,您甚至都无法按代码点输入它们。您不能\u0022用于第一个,因为Java预处理器使该语法错误。因此,您\u0022转而转到,直到下一个\u00E8都不能用这种方式输入,否则它将起作用,直到无法进入下一个,否则它将破坏CANON_EQ标志。最后一个纯粹是一场噩梦:它的代码点是U + 1D402,但是Java不支持使用正则表达式中它们的代码点号来支持完整的Unicode集,这迫使您弄清计算器以找出那是\uD835\uDC02或\uD835\uDC02(但不是\uD835\uDC02),非常疯狂。但是由于设计错误,您不能在字符类中使用这些字符,从而使说不准匹配,[\N{MATHEMATICAL BOLD CAPITAL A}-\N{MATHEMATICAL BOLD CAPITAL Z}]因为正则表达式编译器将UTF-16搞砸了。同样,这永远无法解决,否则它将更改旧程序。通过使用编译Java来解决Java的Unicode-in-source-code的问题,您甚至无法解决该错误java -encoding UTF-8,因为愚蠢的事情将字符串存储为讨厌的UTF-16,这必定会在字符类中将其破坏。 糟糕!

Java缺少了许多其他语言要依赖的正则表达式。没有命名组的示例,甚至没有相对编号的组。这使得从较小的模式构造较大的模式从根本上容易出错。有一个前端库,使您可以使用简单的命名组,实际上,这最终会在生产JDK7中出现。但是即使如此,也没有任何机制可以处理多个同名小组。而且,您仍然没有相对编号的缓冲区。我们又回到了糟糕的过去,这些东西早已解决了。

不支持换行符序列,它是标准中仅有的两个“强烈推荐”的部分之一,建议将\R其用于此目的。由于其可变长度的性质以及Java对字素的支持,这很难模仿。

字符类转义不适用于Java的本机字符集!是的,这是正确的:诸如\wand \s(或更确切地说,”\w”and “\b”)之类的常规内容不适用于Java中的Unicode!这不是很酷的复古。更糟糕的是,Java的\b(使that”\b”与不相同”\b”)确实具有一些Unicode敏感性,尽管不是标准所要求的。因此,例如像绳子”élève”从来没有在Java将匹配的模式\b\w+\b,而不是仅仅在每全部Pattern.matches,但确实是在没有任何一点你可能从得到Pattern.find。这简直就是乞be信仰。他们破坏了\w和之间的固有联系\b,然后误定义了它们来引导!它甚至不知道什么是Unicode字母代码点。这是最严重的问题,他们永远无法修复,因为这会改变现有代码的行为,这在Java Universe中是严格禁止的。最好的办法是创建一个重写库,该库在进入编译阶段之前就充当前端。这样,您就可以将模式从1960年代强制迁移到21世纪的文本处理。

支持的唯一两个Unicode属性是“常规类别”和“块”属性。一般的类别属性只支持类的缩写\p{Sk},违背了标准的强烈推荐中,也允许\p{Modifier Symbol},\p{Modifier_Symbol}等你甚至不得到所需要的别名标准说,你应该。这使您的代码更加难以理解和难以维护。您最终将获得对生产JDK7中的Script属性的支持,但这仍然严重不足标准规定的11个基本属性集,即使是最低级别的Unicode支持,该11个基本属性也必须提供。

Java确实提供的一些微不足道的属性是人造amis:它们具有与正式Unicode属性名称相同的名称,但它们的作用完全不同。例如,Unicode要求\p{alpha}与Unicode相同\p{Alphabetic},但是Java仅使其成为过时的且不再古雅的7位字母,这超过4个数量级,太少了。空格是另一个缺陷,因为您使用伪装为Unicode空格的Java版本,因此您的UTF-8解析器将因为其NO-BREAK SPACE代码点而中断,Unicode通常将其视为空格,但是Java忽略了这一要求,因此中断了您的解析器。

通常不提供对字素的支持\X。这使得无数您需要和想要对正则表达式执行的常见任务成为不可能。由于Java几乎不支持Unicode属性,因此扩展的字形簇不仅触手可及,甚至无法使用standard来近似旧的传统字形簇(?:\p{Grapheme_Base}\p{Grapheme_Extend}]*)。无法使用字素使得即使最简单的Unicode文本处理也无法实现。例如,无论Java中的变音符号如何,都无法匹配元音。您在带有字素支持的语言中执行此操作的方式各不相同,但至少您应该能够将其放入NFD中并进行匹配(?:(?=[aeiou])\X)。在Java中,您甚至不能做那么多:字素超出了您的范围。这意味着Java甚至无法处理自己的本机字符集。它为您提供Unicode,然后使其无法使用。

String类中的便捷方法不缓存已编译的正则表达式。实际上,不存在诸如在编译时进行语法检查的编译时模式之类的东西,即应该在语法检查时进行。这意味着您的程序只使用在编译时完全理解的恒定正则表达式,如果您忘记了此处的反斜杠,或者由于前面讨论的缺陷而不会做反斜杠,那么它将在运行中发生异常炸弹。甚至Groovy都能正确地完成这一部分。正则表达式的结构太高级了,无法用Java的令人讨厌的事后螺栓连接模型处理-对常规文本处理来说,它们太重要了,不容忽视。Java对于这种语言来说太底层了,它无法提供简单的机制来使您自己构建所需的东西:您无法从这里得到实现。

在String和Pattern类被标记final在Java中。这完全消除了使用适当的OO设计扩展这些类的任何可能性。您不能通过matches子类化和替换来创建更好的方法版本。哎呀,你甚至不能继承!最终不是解决方案;最后的是死刑判决,不得上诉。

最后,为了向您展示大脑受损的Java正则表达式的真实程度,请考虑这种多行模式,该模式显示了许多已经描述的缺陷:

String rx =
“(?= ^ \p{Lu} [_\pL\pM\d\-] + \$)\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”
+ ” | Beckman \n”
+ ” | Bayer \n”
+ ” ) # end alternatives \n”
+ ” \b # only on a word boundary \n”
+ “) # end negated lookahead \n”
;
你看到那是多么不自然吗?您必须在字符串中添加文字换行符;您必须使用非Java注释;由于多余的反斜杠,您无法排列任何内容;您必须使用在Unicode上无法正常使用的事物的定义。除此之外,还有许多其他问题。


不仅没有解决这些严重缺陷的计划,而且几乎不可能修复几乎所有这些缺陷,因为您要更改旧程序。即使是普通的OO设计工具也被禁止使用,因为它们都被死刑判决的终结所锁定,并且无法修复。

所以阿里努里,如果你觉得Java的正则表达式笨拙过于大清洗可靠,便捷的正则表达式处理永远是在Java中有可能,我不能反驳你。抱歉,这就是事实。

“已在下一版本中修复!”
仅仅因为某些事情永远无法解决并不意味着什么都无法解决。只是必须非常小心地完成。我所知道的东西已经在当前的JDK7或建议的JDK8构建中得到修复:

  1. 现在支持Unicode脚本属性。您可以使用任何的等价形式\p{Script=Greek}\p{sc=Greek}\p{IsGreek},或\p{Greek}。这在本质上优于旧的笨重块属性。这意味着您可以执行[\p{Latin}\p{Common}\p{Inherited}],这很重要。

  2. UTF-16错误具有解决方法。现在,您可以使用\x{⋯}符号通过编号指定任何Unicode代码点\x{1D402}。即使在字符类中也可以使用,最终可以[\x{1D400}-\x{1D419}]正常工作。不过,您仍然必须加倍反斜杠,并且它只能在regexex中工作,而不是通常应使用的字符串。

  3. 现在,通过标准符号支持命名组(?<NAME>⋯)来创建和\k<NAME>反向引用它。这些也仍然有助于数字组号。但是,您不能以相同的模式获得多个其中的一个,也不能将它们用于递归。

  4. 一种新的模式编译标志,Pattern.UNICODE_CHARACTER_CLASSES以及相关的嵌入式交换机(?U),现在会掉周围的一切事物一样的定义\w,\b,\p{alpha},和\p{punct},使他们现在符合的Unicode标准所要求的那些东西的定义。

  5. 现在支持缺少或定义不正确的二进制属性\p{IsLowercase}\p{IsUppercase}\p{IsAlphabetic},它们对应于Character类中的方法。这很重要,因为Unicode在纯字母与大小写或字母代码点之间做出了重要而普遍的区分。这些关键属性是11类必需的,这些11个必需属性是符合UTS#18的1级标准“ Unicode Regular Expresions”所绝对必需的,否则,您实际上无法使用Unicode。

这些增强和修复对最终拥有非常重要,因此,我很高兴,甚至很高兴拥有它们。

但是对于工业强度,最新的正则表达式和/或Unicode工作,我不会使用Java。如果您敢于使用Java提供的字符集,那么Java的20年后仍不完善的Unicode模型中缺少太多内容,无法完成实际工作。而且,螺栓连接式模型永远无法使用,而所有Java regexe都是如此。您必须从Groovy的工作原理重新开始。

当然,它可能适用于非常有限的应用程序,这些应用程序的小客户群仅限于爱荷华州农村地区的英语母语群体,无需外部交互,也无需使用老式电报可以发送的字符。但对于有多少项目是这是真的吗?事实证明,您所想的甚至更少。

正是由于这个原因,最近才取消了某个(而且很明显)数十亿美元的重要应用程序的国际部署。Java的Unicode支持(不仅在正则表达式中,而且在整个过程中)都太弱了,以至于无法在Java中可靠地完成所需的国际化。因此,他们被迫从最初计划的全球范围部署缩减​​为仅在美国部署。肯定是狭och的。不,有NᴏᴛHᴀᴘᴘʏ;你会吗

Java已经有20年的时间使它正确,而且显然到目前为止他们还没有做到,所以我不会屏息。或把好钱丢在坏处;这里的教训是忽略炒作,而是适用尽职调查做出非常确保所有必要的基础设施支持,是有之前你投入太多。否则,一旦您无法解决项目,也可能会陷入没有任何实际选择的困境。



 类似资料:
  • 如何解析和添加两个值?我使用这种模式: 包括间距,我需要得到“13” 使用正则表达式是可能的,还是有其他方法可以轻松实现这一点? 提前感谢你的帮助。

  • 本文向大家介绍正则表达式实现添加、删除、替换三种功能,包括了正则表达式实现添加、删除、替换三种功能的使用技巧和注意事项,需要的朋友参考一下 最近呢,公司让写一个组件,要求用正则表达式构建一个中文的匹配组件,许多问题就来了,大多是用自动机来构建一个正则表达式的引擎,小编虽然大学的时候学过编译原理,可奈何现在已经忘得差不多了,只能通过现有的正则表达式来进行功能实现。 下面的代码呢实现了通过用正则表达式

  • 我有以下代码: 代码JS: 运行此代码后,具有以下形式 我想在最后一个括号前添加空格,如下例所示。 您能告诉我如何接受对代码的更改,以便保留空白吗? 提前感谢! 编辑: 我添加了这个代码来调用这里作为添加空白。。。

  • 问题内容: 我已经在Java的正则表达式实现中发现了一些意外行为。当使用和时,在使用Matcher的方法时,以下正则表达式在输入上 不 正确匹配: 如果更改最外层匹配组中表达式的顺序,则Matcher的方法 会 匹配。 这是一些说明此问题的测试代码。 RegexTest.java 预期的输出是: 但是实际输出是: 我已经验证了Ubuntu Linux上的Java版本1.7.0_11和OSX 10.

  • 我希望在我的项目中使用正则表达式模式进行验证。 下面是正则表达式模式的描述: > 长度必须至少为8个字符。 必须至少包含1个大小写字符。 必须包含至少1个小写字符。 必须包含至少1个数字。 可能包含以下字符: " 不得使用重复字符。(aa、11等) 不得使用超过3个连续字符。(abcd、wxyz、1234等)

  • 问题内容: 我遇到过这种模式 并从http://gskinner.com/RegExr/站点了解到= =匹配字母数字字符和下划线,并且=匹配先前的令牌1次或多次(不确定确切的含义)。 如何将连字符添加到列表中? 我试过了但是没用,我没有任何匹配… 问题答案: 您需要一个字符类,用表示。然后可以在字符类中使用它,并可以添加更多字符: 不过请小心,如果您添加更多字符以进行匹配。连字符减号必须在类中的