在 Ruby 中从字符串中提取数字

问题描述 投票:0回答:8

我正在使用此代码:

s = line.match( /ABCD(\d{4})/ ).values_at( 1 )[0] 

从字符串中提取数字,例如:

ABCD1234
ABCD1235
ABCD1236

等等。

它可以工作,但我想知道在 Ruby 中我还有什么其他选择?

我的代码:

ids = [] 
someBigString.lines.each {|line|
   ids << line.match( /ABCD(\d{4})/ ).values_at( 1 )[0] 
}
ruby
8个回答
217
投票

有很多 Ruby 方法,如 http://www.ruby-forum.com/topic/125709

  1. line.scan(/\d/).join('')
  2. line.gsub(/[^0-9]/, '')
  3. line.gsub(/[^\d]/, '')
  4. line.tr("^0-9", '')
  5. line.delete("^0-9")
  6. line.split(/[^\d]/).join
  7. line.gsub(/\D/, '')

在控制台上尝试每个操作。

另请查看该帖子中的基准报告。


67
投票

还有更简单的解决方案

line.scan(/\d+/).first

42
投票
a.map {|x| x[/\d+/]}

19
投票

最简单、最快的方法是使用正则表达式从字符串中解析出整数。

str = 'abc123def456' 

str.delete("^0-9")
=> "123456"

str.tr("^0-9","")
=> "123456"

将长字符串上的基准与此处提供的一些其他解决方案进行比较,我们可以看到

tr
最快,
delete
接近第二。

require 'benchmark'

n = 2 
string = [*'a'..'z'].concat([*1..1_000_000].map(&:to_s)).shuffle.join

Benchmark.bmbm do |x|
  x.report('scan') do
    n.times {string.scan(/\d/).join}
  end
  x.report('gsub') do
    n.times {string.gsub(/[^\d]/,"")}
  end
  x.report('gsub2') do
    n.times {string.gsub(/\D/, '')}
  end
  x.report('tr') do
    n.times {string.tr("^0-9","")}
  end
  x.report('delete') do
    n.times {string.delete('^0-9')}
  end
  x.report('split') do
    n.times {string.split(/[^\d]/).join}
  end
end

Rehearsal ------------------------------------------
scan     3.509973   0.187274   3.697247 (  3.717773)
gsub     0.229568   0.002790   0.232358 (  0.233961)
gsub2    0.307728   0.013435   0.321163 (  0.390262)
tr       0.021395   0.000223   0.021618 (  0.022297)
delete   0.021290   0.002280   0.023570 (  0.024365)
split    0.284935   0.010274   0.295209 (  0.299865)
--------------------------------- total: 4.591165sec

             user     system      total        real
scan     3.146615   0.126009   3.272624 (  3.281860)
gsub     0.211263   0.001515   0.212778 (  0.213170)
gsub2    0.208482   0.000424   0.208906 (  0.209339)
tr       0.015228   0.000086   0.015314 (  0.015387)
delete   0.015786   0.000128   0.015914 (  0.016050)
split    0.205096   0.002736   0.207832 (  0.208380)


4
投票
your_input = "abc1cd2"
your_input.split(//).map {|x| x[/\d+/]}.compact.join("").to_i

这应该有效。


2
投票

另一种解决方案可能是写:

myString = "sami103"
myString.each_char{ |c| myString.delete!(c) if c.ord<48 or c.ord>57 } #In this case, we are deleting all characters that do not represent numbers.

现在,如果你输入

myNumber = myString.to_i #or myString.to_f

这应该返回一个


0
投票

数字字符串可以是小数和/或负数。因此:

  [string.chr == "-" ? "-" : nil, string.delete("^.0-9")].compact.inject(:+)

这利用

chr
方法 来检查字符串是否以负号
-
开头。然后它会删除除数字和小数之外的所有内容,然后在前面添加
-
(如果最初存在)。

这允许使用

to_i
to_f
方法进行链接。

  • compact
    从数组中删除 nil(如果它不是负数)
  • inject(:+)
    根据数组内容的顺序将数组展平回字符串

如果分数也是并且问题

[
  chr == "-" ? "-" : nil,
  gsub(%r{[^./0-9[:space:]]}, "").gsub(%r{[[:space:]]*(?<numerator>\d+)[[:space:]]*/[[:space:]]*(?<denominator>\d+)[[:space:]]*}) do
    $LAST_MATCH_INFO.named_captures.with_indifferent_access.then do |capture_hash|
      Rational(capture_hash.fetch(:numerator, 0), capture_hash.fetch(:denominator, 1)).to_f.to_s.delete_prefix('0')
    end
  end
].compact.inject(:+).squish

这是假设“真分数”

此版本将

/
[[:space:]]
添加到例外列表中,以指示除法将在哪里进行,并允许我们利用空格来确定分数的大小。

gsub(%r{[[:space:]]*(?<numerator>\d+)[[:space:]]*/[[:space:]]*(?<denominator>\d+)})
是一个正则表达式,用于查找:

  1. [[:space:]]*
    -> 零个或多个空格
  2. (?<numerator>\d+)
    分子的一组数字
  3. [[:space:]]*
    -> 零或更多空间
  4. (?<denominator>\d+)
    一组数字作为分母
  5. [[:space:]]*
    -> 零或更多空间

$LAST_MATCH_INFO.named_captures
为我们提供了命名捕获组和匹配值的哈希值:

"3 1 / 2 cups".gsub(%r{[^./0-9[:space:]]}, "").gsub(%r{[[:space:]]*(?<numerator>\d+)[[:space:]]*/[[:space:]]*(?<denominator>\d+)[[:space:]]*}) do
  $LAST_MATCH_INFO.named_captures # => {"numerator"=>"1", "denominator"=>"2" }
end

使用此哈希,我们可以

fetch
并设置默认值,以便在未找到分子和分母时
Rational
始终解析为 0。

.to_f.to_s.delete_prefix('0')
假设分数的分子始终小于分母。这部分代码将分数转换为浮点数,然后转换为字符串,之后 if 从字符串中删除第一个
char
,该字符串预计为零。

Rational(1, 2).to_f                         # => 0.5
Rational(1, 2).to_f.to_s                    # => "0.5"
Rational(1, 2).to_f.to_s.delete_prefix('0') # => ".5"

delete_prefix 的基准测试接近最快,但更具可读性,因此我选择它。

最后我们有一个

squish
来清理任何前导或尾随空白,如果您要使用更纯粹的 ruby,也可以使用
strip

注意:如果您在此处添加

to_i
to_f
方法,则不需要
squish
strip
,因为它们对空格并不挑剔,并且在没有空格的情况下也能很好地解析数字。所以你最终可以得到这样的东西来为你添加这些方法:

def numberize(with: :squish)
  raise ArgumentError, "with: #{with} is not a valid argument" unless with.in?(%i[squish strip to_i to_f])

  [
    chr == "-" ? "-" : nil,
    gsub(%r{[^./0-9[:space:]]}, "")
      .gsub(%r{[[:space:]]*(?<numerator>\d+)[[:space:]]*/[[:space:]]*(?<denominator>\d+)[[:space:]]*}) do
        $LAST_MATCH_INFO.named_captures.with_indifferent_access.then do |capture_hash|
          Rational(capture_hash.fetch(:numerator, 0), capture_hash.fetch(:denominator, 1)).to_f.to_s.delete_prefix('0')
        end
      end
  ].compact.inject(:+).send(with)
end

-1
投票

要从字符串中提取数字部分,请使用以下命令:

str = 'abcd1234'
/\d+/.match(str).try(:[], 0)

它应该返回

1234

© www.soinside.com 2019 - 2024. All rights reserved.