在 Python 和 Java 中使用编译的正则表达式的好处

无论您使用 Java 还是 Python 进行编程,编译的正则表达式都可以显着加快您的文本操作例程。

我参与了最近关于从 URL 字符串中删除给定参数的“最佳”方法的讨论。

对话从使用字符串原语来拆分和连接参数开始,这种方法清晰易读。使用一两行正则表达式替换可以达到相同的结果,但反驳是正则表达式或regex非常慢。这通常是正确的。

尽管如此,我想知道正则表达式与字符串基元相比慢了多少,以及使用预编译的正则表达式是否可以提高性能。

使用字符串操作删除参数

删除参数的最简单且可以说是最易读的方法是使用 split 原语拆分字符串,删除给定的参数,然后重新加入字符串。在 Python 和 Java 中使用编译的正则表达式的好处

这对我来说是一项繁重的工作,而且考虑到字符串在大多数语言中都是不可变的,因此可能会占用大量内存。

在 Java 中,这相当于四行代码:

String[] parts = input.split("&");
List<String> partsList = new ArrayList<>(Arrays.asList(parts));
partsList.removeIf(part -> part.contains("option"));
outputsSplit = String.join("&",partsList);

在Python中,代码更短、更简洁:

parts = input.split("&")
parts = [part for part in parts if "option" not in part]
output = '&'.join(parts) 

两者都做同样的事情:操作并重新加入拆分列表。

使用正则表达式删除参数

Java 或 Python 中删除参数的另一个选项是使用正则表达式删除输入字符串的匹配部分。此方法的可读性较差,但使用的代码行数较少。这是正则表达式非常适合的用例。

在 Java 中,这需要一行代码:

output = input.replaceFirst("option=one$","").replaceFirst("option=one&",""); 

在 Python 中,它相当于两行代码:

output = re.sub(r"option=one$", "", input)
output = re.sub(r"option=one&", "", output) 

Pythonic 方式比字符串基元使用更少的代码行。这是正则表达式的一个非常适合的用例,尽管问题是正则表达式非常慢。

提前知道这一点,你能做什么?还有另一种方法。

编译正则表达式以删除参数

当用于匹配要删除的部分的正则表达式没有更改时,您可以使用所谓的编译正则表达式

将标准正则表达式视为在字符串中查找内容的秘诀。计算机每次使用菜谱时都必须读取菜谱并进行解析。这通常是使用正则表达式时最消耗性能的部分。

已编译的正则表达式只执行一次,这使得它们比标准正则表达式快得多,尽管如果模式更改则无法使用它们。

这里的缺点是代码行数比原始字符串基元版本有所增加,这是使用正则表达式的最初动机。

在 Java 中,我们最终得到六行:

private static final Pattern PATTERN_END = Pattern.compile("&option=one$");
private static final Pattern PATTERN_START_MIDDLE = Pattern.compile("option=one&");
Matcher matcherEnd = PATTERN_END.matcher(input);
String intermediateResult = matcherEnd.replaceFirst("");
Matcher matcherStartMiddle = PATTERN_START_MIDDLE.matcher(intermediateResult);
output = matcherStartMiddle.replaceFirst(""); 

在 Python 中,需要四行:

PATTERN_END = re.compile(r"&option=one$")
PATTERN_START_MIDDLE = re.compile(r"option=one&")
intermediate_result = PATTERN_END.sub('', input)
output = PATTERN_START_MIDDLE.sub('', intermediate_result) 

那么,真正的问题是:代码行数的增加值得性能提升吗?

为了直接测试这些方法,我编写了一些代码来生成 100,000 个输入行,并在其中随机放置了我们要删除的参数。然后我通过函数运行代码并测量完成时间。

带有已编译正则表达式的 Java 结果

在 Java 中,标准正则表达式的性能与使用字符串原语的性能如此接近,令我感到惊讶。字符串原语需要 188 毫秒来处理 100,000 行。标准正则表达式需要 224 毫秒才能处理同样的 100,000 行。

另一方面,编译的正则表达式却令人惊叹不已。他们设法在短短 52 毫秒内处理了 100,000 行。这比字符串基元快三倍多。

带有已编译正则表达式的 Python 结果

在 Python 中使用相同的方法随机生成 100,000 个输入行,结果显示出与 Java 的一些差异。首先,代码总体上慢了近五倍。与编译语言相比,解释语言会出现这种情况

在第二个例子中,使用字符串基元和正则表达式之间存在更大的差异。使用正则表达式的速度几乎是字符串基元的两倍。字符串原语在 477 毫秒内处理了 100,000 行,而标准正则表达式则用了 904 毫秒。

不过,编译后的正则表达式在 Python 中的表现仍然相对较好。该方法在 165 毫秒内处理了 100,000 行,大约比 Python 中的字符串基元快两倍。

结论

所有这一切的结果是,是的,标准正则表达式比简单的字符串基元要慢得多。然而,差异的程度在很大程度上取决于您使用的编码语言。

此外,如果您编写高性能字符串替换代码,那么编译的正则表达式是最佳选择。

David“Walker”Aldridge 是一位在多种语言和远程编程方面拥有 40 年经验的程序员。他也是一位经验丰富的系统管理员和信息安全蓝队成员,对逆计算感兴趣。