如何使用Java正则表达式匹配长字符串?

9

我知道可以使用Pattern.compile("\\d*");匹配数字。

但它不能处理长整型的最大/最小值。

出于与异常相关的性能问题,我不想尝试解析长整型,除非它确实是一个长整型。

if ( LONG_PATTERN.matcher(timestampStr).matches() ) {
    long timeStamp = Long.parseLong(timestampStr);
    return new Date(timeStamp);
} else {
    LOGGER.error("Can't convert " + timestampStr + " to a Date because it is not a timestamp! -> ");
    return null;
}

我不想使用try/catch块,也不想让异常在Java long的大小之外长时间抛出,例如"564654954654464654654567879865132154778"。
有没有人有处理原始Java类型这种需求的模式? JDK是否提供自动处理的功能? Java中是否有故障安全的解析机制?
谢谢
编辑: 请假设"错误的long字符串"不是一个特殊情况。 我不需要一项基准测试,我只需要一个表示长整型数字的正则表达式。 我知道正则表达式检查需要额外的时间,但至少我的长整型解析将始终保持恒定,并且永远不会依赖于“坏的长整型字符串”的百分比。
我找不到链接了,但在StackOverflow上有一个漂亮的解析基准测试,清楚地显示重用相同编译的正则表达式非常快,比抛出异常要快得多,因此仅有少量的异常将使系统比添加附加正则表达式检查更慢。

4
请注意,"\d*" 也会匹配空字符串。 - Bart Kiers
可能你的问题已经被问过了。在我的看法中,异常会比正则表达式更快。 - Miki
@Sorrow:之前的问题你抓得好。关于异常和正则表达式:你是怎么想的?抛出异常不是一个快速的过程。一旦编译完成,正则表达式就非常快了。 - T.J. Crowder
1
正如我在答案中所述,一切取决于异常情况发生的频率。使用模式匹配时,您需要一直做额外的工作,而使用异常处理时,只有在引发异常时才需要做额外的工作。 - gexicide
@Sorrow,预编译的模式将比需要捕获的异常更有效。但是当Long.parseLong(...)不会抛出异常时,适合于long的(字符串)数字检查速度更快。 - Bart Kiers
1
我同意@gexicide的观点 - 一切都取决于异常被捕获的频率,因为无论如何正则表达式都会被检查。在我看来,将T.J. Crowder回答的-?\\d{1,19}与异常捕获相结合应该是最优的选择。 - Miki
3个回答

18

long类型的最小值为-9,223,372,036,854,775,808,最大值为9,223,372,036,854,775,807。因此,最多有19位数字。\d{1,19}可以匹配到这些数字,可能会带有可选的负号-,使用^$来匹配字符串的开头和结尾。

简而言之:

Pattern LONG_PATTERN = Pattern.compile("^-?\\d{1,19}$");

如果你不允许逗号(或已经去除了逗号),可以使用类似于上面的代码来验证一个整数。

如gexicide在评论中指出的那样,上述方法允许一些数量较小(相对而言)的非法值,例如9,999,999,999,999,999,999。你可以使用更复杂的正则表达式,或者只接受以上方法可以筛选掉大多数非法数字的事实,从而减少解析异常的数量。


不是真的,您的模式允许数字9,999,999,999,999,999,999,这不在长整型范围内。 - gexicide
1
@gexicide:没错,可能需要进行一些调整。但至少可以大大减少异常的数量。 - T.J. Crowder
当然,但我猜OP想要一个正确的解决方案。但我同意,该模式已经过滤了大多数错误情况。 - gexicide
谢谢,这正是我也在考虑的——限制数字位数。 - Sebastien Lorber
@SebastienLorber: -是一个字面值,表示允许负数,因此你在Javadoc中没有找到它。 :-) 它后面的?表示“零或一次”,这在Javadoc中有说明。例如, ?{0,1} 的简写。 - T.J. Crowder
显示剩余2条评论

3

这个正则表达式应该可以满足您的需求:

^(-9223372036854775808|0)$|^((-?)((?!0)\d{1,18}|[1-8]\d{18}|9[0-1]\d{17}|92[0-1]\d{16}|922[0-2]\d{15}|9223[0-2]\d{14}|92233[0-6]\d{13}|922337[0-1]\d{12}|92233720[0-2]\d{10}|922337203[0-5]\d{9}|9223372036[0-7]\d{8}|92233720368[0-4]\d{7}|922337203685[0-3]\d{6}|9223372036854[0-6]\d{5}|92233720368547[0-6]\d{4}|922337203685477[0-4]\d{3}|9223372036854775[0-7]\d{2}|922337203685477580[0-7]))$

但是,这个正则表达式没有验证其他符号,例如+L_等。如果您需要验证所有可能的长整数值,您需要升级此正则表达式。


1

只需捕获NumberFormatException,除非这种情况经常发生。

另一种方法是使用仅允许长整型字面量的模式。这样的模式可能相当复杂。

第三种方法是首先将数字解析为BigInt。然后,您可以将其与Long.MAX_VALUE和Long.MIN_VALUE进行比较,以检查它是否在long的范围内。但是,这也可能很昂贵。

还要注意: 解析长整型非常快,它是一种非常优化的方法(例如,尝试在一步中解析两个数字)。应用模式匹配可能比执行解析更加昂贵。解析速度慢的唯一问题是抛出NumberFormatException。因此,如果异常情况不经常发生,只需捕获异常即可。


OP说:“对于与异常相关的性能问题,除非它确实是一个长整型,否则我不想尝试解析长整型。” - T.J. Crowder
我的想法是为该方法获取一个恒定的执行时间,而不是由于错误的长整型值的存在而导致的执行时间。据我所知,我在SO上读过一个基准测试和正则表达式,当它们不是每次重新编译时,速度非常快。 - Sebastien Lorber
жҲ‘жӯЈеңЁйҳ…иҜ»parseLongзҡ„жәҗд»Јз ҒпјҲhttp://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/Long.java#Long.parseLong%28java.lang.String%2Cint%29пјүпјҢдҪҶжҳҜжҲ‘жІЎжңүжүҫеҲ°д»»дҪ•и§ЈжһҗдёӨдҪҚж•°еӯ—зҡ„иҜҒжҚ®гҖӮ - Marko Topolnik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接