如何检查一个字符串是否为数字(Julia)

11

一直在搜寻互联网上的信息,试过了isnumeric,但只适用于AbstractChar。如果可能的话,我宁愿不使用tryparse,但如果这是唯一的解决方案,那就这样吧... 如果是这样,为什么还没有实现一个检查字符串是否为数字的函数呢?


5
为什么不使用“tryparse”? - DNF
"tryparse" 是正确的 Julia 内置函数。没有任何可能的实现方式不涉及尝试将字符串解析为数字。 - Michael K. Borregaard
4
“numeric” 是什么意思?1.0e2 是数字吗?6.2f22 是数字吗?0x02 呢?还有 0b1001010xdead_beef.cap23 呢? - mbauman
@DNF 因为对我个人来说感觉有点像黑客攻击。 - Masterfoxify
@MattB。我的意思只是整数,但从理论上讲,我正在寻找一种检查任何数值的方法。 - Masterfoxify
@Masterfoxify 嗯,你可以尝试使用正则表达式:isintstring(str) = !isnothing(match(r"^\d*$", str)),或者类似的方法。但是它会更慢。我猜你认为 tryparse 很麻烦,因为你没有真正意识到这是如何完成的,从概念上讲。(编辑:哎呀,完全错过了下面的实际回复。) - DNF
4个回答

11

我找到的最快解决方案是使用建议的tryparse方法。

function check_str2(a)
    return tryparse(Float64, a) !== nothing
end

与正则表达式的40ns相比,它平均为20ns。

没有办法在不进行转换的情况下检查字符串是否有效,主要原因是在性能关键的地方实际上没有多少强制性的用例需要这样做。 在大多数情况下,您希望知道某些东西是否可以解析为数字以将其用作数字,而在罕见的情况下,额外的几个纳秒可能并不重要。


我使用了benchmarktools中的@benchmark。我正在使用julia 1.1.1版本。 - Oscar Smith
1
然而,你的解决方案不起作用,“check_str2(“60.0xxx”)”返回“true”,但“60.0xxx”不是数字。 “Parsers.tryparse”的问题可能在于它尝试从“String”中提取数字,并且只有在数字位于开头时才会成功。 - Przemyslaw Szufel
据我的测试所示,以上是错误的。 - Masterfoxify
在任何最近的Julia版本(即1.0之后),check_str2("60.0xxx")返回false。 (我很惊讶它曾经返回true)。 - kmsquire
实际上,Parsers.jl 包中的 Parsers.tryparse 确实具有 @PrzemyslawSzufel 指出的行为。然而,在 Julia 的 Base 中,tryparse 并没有相同的行为(即它可以工作),并且实际上比下面的 check_str 更快。我编辑了这个解决方案,使用了 Base 版本。 - kmsquire
Base.tryparse 仍然比我的答案慢2倍。因此,正则表达式是这里的最佳选择。 - Przemyslaw Szufel

5

通常你可以使用正则表达式来检查一个字符串是否为数字:

julia> re = r"^[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)$";

julia> occursin(re,"123.")
true

julia> occursin(re,"123.0")
true

julia> occursin(re,"123.012")
true

julia> occursin(re,"123")
true 

julia> occursin(re,"ab")
false

julia> occursin(re,"ab123.1")
false

julia> occursin(re,"123.1e")
false

注意:我使用了在浮点数正则表达式找到的普通表达式。如果您只想要整数部分或包括指数,这样的准备好的正则表达式也很容易找到。
编辑:基准测试。
让我们考虑以下函数来检查一个字符串是否是数字:
function check_str(a)
    try
        parse(Float64,a)
        true
    catch
        false
    end
end

以下是基准测试结果。请注意,正则表达式大约快了 200 倍(如果我们还决定查找指数部分,则增加量将更小),而且不会分配内存。

julia> using BenchmarkTools

julia> @btime check_str("60.0a")
  15.359 μs (18 allocations: 816 bytes)
false

julia> @btime occursin($re,"60.0a")
  67.023 ns (0 allocations: 0 bytes)
false

当成功解析String时,速度差距要小得多:

julia> @btime check_str("60.0")
  298.833 ns (0 allocations: 0 bytes)
true

julia> @btime occursin($re,"60.0")
  58.865 ns (0 allocations: 0 bytes)
true

这种方法有什么优势,超过了 tryparse 吗? - Oscar Smith
它大约快了200倍,代码更短且不需要分配内存。我会在我的回答中更新一个例子。 - Przemyslaw Szufel
如果您使用TryParse而不是从Parse捕获异常,平均只需20ns(相比于正则表达式的30ns)。 - Oscar Smith
了不起的工作@PrzemyslawSzufel!不过,我会记住Oscar Smith关于时间的评论。目前我的问题不需要超级硬核优化,所以无论如何都应该没问题。虽然这个答案绝对令人惊叹,并且付出了很多辛勤的努力,但是Oscar Smith的答案更加实用,因为它还考虑了十六进制和科学计数法,正如Matt B指出的那样。 - Masterfoxify
Oscar的答案不起作用(请看下面我的评论)-它可能会对像“60xxx”或60.0xxx这样的字符串返回错误结果。您应该使用Base.parse(比正则表达式慢)或正则表达式。 - Przemyslaw Szufel
@PrzemyslawSzufel 我在自己的Julia控制台中测试了你说的内容,但没有得到相同的结果。事实上,它完美地运行了。 - Masterfoxify

3
这对我行得通:
isa(tryparse(Float64,"StringNumber"), Number)    # true | false

2

正如OP在评论中所建议的,他们只需要检查整数,你仍然可以使用isnumeric(或者可能更好地使用isdigit):

isintstring(str) = all(isdigit, str)

这个方法似乎比其他答案都要快。在我的电脑上,它的基准测试速度大约比 tryparse 解决方案快2倍。


1
这是它们中最快的。 - papabiceps

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