Java最高

使用Spring 5和Spring Boot 2开始,通过学习的春天课程:

>>查看课程

1.概述

当我们需要在Java中查找或替换字符串中的值时,我们通常使用常用表达。这些允许我们确定是否字符串的部分或全部匹配一种模式。我们可能很容易用弦中的多个令牌应用相同的替换全部替换方法同时匹配细绳

在本教程中,我们将探索如何对字符串中找到的每个令牌应用不同的替换。这将使我们更容易满足一些用例,如转义某些字符或替换占位符值。

我们还将研究一些调整正则表达式以正确识别标记的技巧。

2.单独处理比赛

在我们可以构建我们的令牌替代算法之前,我们需要了解常规表达式周围的Java API。让我们使用捕获和非捕获组来解决一个棘手的匹配问题。

2.1。标题案例示例

假设我们想要构建一个算法来处理字符串中的所有标题词。这些单词以一个大写字符开始,然后以小写字符结束或继续。

我们的投入可能是:

“前3个重要的词!”然后我找到了10个TLAs”

从标题词的定义,这包含匹配:

  • 第一的
  • 资本
  • 一世
  • 发现

和识别此模式的正则表达式是:

“(?<= ^ | [^ a-za-z])([a-z] [a-z] *)(?= [^ a-za-z] | $)”

要了解这一点,让我们将其分解为其组件部分。我们将从中间开始:

[A-Z]

将识别一个大写字母。

我们允许单字符单词或后跟小写字母的单词,因此:

[a-z] *

识别零个或多个小写字母。

在某些情况下,上述两个字符类就足以识别我们的令牌。不幸的是,在我们的示例文本中,有一个以多重大写字母开头的单词。所以,我们需要表达我们发现的单一大写字母必须是第一个在非字母后出现的首先出现。

同样,正如我们允许单一的大写字母单词,我们需要表达我们发现的单一大写字母一定不是多重资本字母单词。

表达式[^ A-Za-z]意思是“不信”。我们把其中一个放在非捕获组的表达式开头:

(? < = ^ | [^ A-Za-z])

非捕获组,开始(?<=,A.向后看以确保匹配出现在正确的边界。它的对等物在最后为后面的字符做同样的工作。

但是,如果单词触摸字符串的非常开始或结尾,那么我们需要解释一下,这是我们添加的地方^ |到第一个组使其意味着“字符串的开始或任何非字母字符”,我们已添加到最后一个非捕获组的末尾,以允许字符串的结尾是边界。

在非捕获组中发现的字符不会出现在比赛中当我们使用

我们应该注意到,即使是这样的简单用例也可以有许多边缘案例,所以测试正则表达式很重要。为此,我们可以编写单元测试,使用我们的IDE的内置工具,或使用在线工具regexr.

2.2。测试我们的例子

使用我们的示例文本以常量调用EXAMPLE_INPUT我们的正则表达式模式被称为title_case_pattern.,让我们使用匹配类以在单元测试中提取我们所有匹配项:

Matcher Matcher = TITLE_CASE_PATTERN.matcher(EXAMPLE_INPUT);List matches = new ArrayList<>();While (matcher.find()) {matches.add(matcher.group(1));} assertThat(matches) . containexactly ("First", "Capital", "Words", "I", "Found");

在这里,我们使用匹配器功能模式生产A.匹配。然后我们使用在循环中的方法直到它停止返回真的迭代所有比赛。

每一次回报真的, 这匹配对象的状态设置为表示当前匹配。我们可以检查整个比赛组(0)或者用基于1的索引检查特定的捕获组。在这种情况下,有一个捕获组围绕着我们想要的部分,所以我们使用组(1)将匹配添加到我们的列表中。

2.3。检查匹配多一点

我们到目前为止设法找到了我们想要处理的单词。

但是,如果每个单词都是我们想要替换的标记,那么我们就需要有更多关于匹配的信息,以便构建结果字符串。金宝搏官网188be我们来看看其他的一些性质匹配这可能对我们有帮助:

while (match .find()) {System.out。println("Match: " + Match .group(0));system . out。println("Start: " + match . Start ());system . out。println("End: " + match . End ());}

这段代码将向我们显示每个匹配项的位置。它还告诉我们组(0)匹配,这是捕获的一切:

Match: First Start: 0 End: 5 Match: Capital Start: 8 End: 15 Match: Words Start: 16 End: 21 Match: I Start: 37 End: 38…更多的

在这里,我们可以看到每场比赛只包含我们期待的单词。开始属性显示匹配零索引在字符串中。这结尾显示在后面的角色的索引。这意味着我们可以使用substring(开始,end-start)要从原始字符串中提取每个匹配。这基本上是如何的团体方法为我们做了这个。

现在我们可以使用为了迭代比赛,让我们来处理我们的令牌。

3.逐一替换匹配项

让我们继续使用我们的算法替换原始字符串中的每个标题单词,并使用其小写等效项替换每个标题字。这意味着我们的测试字符串将转换为:

前3个大写单词!然后我发现了10个TLAs。”

模式匹配类不能为我们做这个,所以我们需要构造一个算法。

3.1。替换算法

以下是算法的伪代码:

  • 从空输出字符串开始
  • 为每个匹配:
    • 将匹配之前和之前任何匹配之后的内容添加到输出中
    • 处理此匹配并将其添加到输出中
    • 继续,直到处理所有匹配项
    • 将最后一个匹配项之后剩下的内容添加到输出中

我们应该注意到这种算法的目的是查找所有非匹配区域并将其添加到输出中以及添加处理后的匹配。

3.2。Java中的令牌替换器

我们要将每个单词转换为小写,因此我们可以编写一个简单的转换方法:

私有静态字符串转换(String令牌){return token.tolowerCase();}

现在我们可以编写算法来迭代匹配。这可以使用aStringBuilder.对于输出:

int lastIndex = 0;StringBuilder输出= new StringBuilder();Matcher Matcher = TITLE_CASE_PATTERN.matcher(original);While (match .find()){输出。附加(original, lastIndex, matcher.start()) .append(convert(matcher.group(1))); lastIndex = matcher.end(); } if (lastIndex < original.length()) { output.append(original, lastIndex, original.length()); } return output.toString();

我们应该注意到StringBuilder.提供方便的版本附加它可以提取子字符串。这适用于结尾的属性匹配让我们拾取上次匹配之后所有不匹配的字符。

4.概括算法

现在我们已经解决了替换某些特定令牌的问题,为什么我们不将代码转换为可用于常规案例的形式?唯一从一个实现变化到接下来的事情是要使用的正则表达式,以及将每个匹配转换为其替换的逻辑。

4.1。使用函数和模式输入

我们可以使用Java功能<匹配,String>对象允许呼叫者提供逻辑来处理每种匹配。我们可以拍摄一个名为tokenPattern要查找所有令牌:

//与之前一样while (matcher.find()){输出。附加(original, lastIndex, matcher.start()) .append(converter.apply(matcher)); // same as before

这里,正则表达式不再是硬编码的。相反,这是转换器函数由调用者提供,并应用于环形。

4.2。测试一般版本

让我们看看常规方法是否有效以及原件:

replaceTokens("前3个大写单词!然后10个TLAs,我发现",TITLE_CASE_PATTERN, match -> match.group(1).toLowerCase())) .isEqualTo("前3个大写单词!然后是10个TLAs,我发现”);

在这里,我们看到调用代码很简单。转换函数易于表达为Lambda。并测试通过。

现在我们有一个令牌替换者,所以让我们尝试一些其他用例。

5.一些用例

5.1。逃避特殊字符

让我们想象我们想要使用正则表达式转义字符\手动引用正则表达的每个性格而不是使用引用方法。也许我们在创建正则表达式时用引号括住一个字符串,以便传递给另一个库或服务,因此用块引号括住这个表达式是不够的。

如果我们可以表示表示“一个正则表达式字符”的模式,那么很容易使用我们的算法来转义它们:

模式regexcharacters = pattern.compile(“[<(\\ [{\\ [{\\\\ ^ \\  -  = $!| \\]})?* +。>]”);assertthat(替换(“像[”,regaxacteracters,匹配的正则表达式,匹配 - >“\\”+ match.group())).isequalto(“像\\ [”这样的正则表达式字符);

对于每场比赛,我们前缀\的性格。作为\是Java字符串中的特殊字符,它被另一个\

实际上,这个例子是额外的\字符作为模式中的字符类regeaxacters.必须引用许多特殊字符。这显示了我们使用它们来表示他们的文字的正则表达式解析器,而不是正则表达式语法。

5.2。替换占位符

表达占位符的常用方法是使用类似的语法$ {name}。让我们考虑一个使用模板的用例“Hi $ {name}以$ {company}”需要从一个调用的地图填充PlaceUrcalues.

地图 placeUrter5rues = new hashmap <>();placeUrcervalues.put(“姓名”,“票据”);PlaceUrcerfalues.put(“公司”,“Baeldun金宝搏188体育g”);

我们所需要的只是一个良好的正则表达找到$ {...}令牌:

“\\ $ \\ {(?<占位符> [a-za-z0-9 -_] +)}”

是一个选择。它必须引用$和初始卷曲支撑,否则它们将被视为正则表达式语法。

在这种模式的核心,是占位符的名称的捕获组。我们使用了一个字符类,允许字母数字,破折号和下划线,这应该适合大多数使用情况。

然而,要使代码更可读,我们将命名为此捕获组占位符。让我们看看如何使用命名的捕获组:

Assertthat(替换(以$ {公司}“,”\\ $ \\ {(? [a-za-z0-9 -_] +)}“,匹配 - > placeUrcervalues。get(match.group(“占位符”)))).isequalto(“Baeldung嗨比尔”);金宝搏188体育

在这里,我们可以看到将命名组的值置于其中匹配只是涉及使用团体使用名称作为输入,而不是数字。

6.结论

在本文中,我们研究了如何使用强大的正则表达式来查找我们的字符串中的令牌。我们了解了方法使用匹配向我们展示比赛。

然后我们创建并概括了一种算法,以允许我们替换令牌更换。

最后,我们看了一些常见用例逃离角色和填充模板。

一如既往,可以找到代码示例在GitHub

Java底部

使用Spring 5和Spring Boot 2开始,通过学习的春天课程:

>>查看课程
对这篇文章的评论关闭!