Как выбрать число с плавающей запятой или целое число с помощью регулярного выражения из текста - PullRequest
1 голос
/ 27 мая 2020

Я хочу сопоставить заданное целое число или число с плавающей запятой из некоторого текста, но ограничено определенной строкой, потому что похожие числа могут появляться в разных местах.

Это то, что у меня в качестве ввода, числа составлены, не пытайтесь соотносить «общее» с «частичными»:

===> Verifying dependencies...
===> Compiling sample
===> Performing cover analysis...
  |------------------------|------------|
  |                module  |  coverage  |
  |------------------------|------------|
  |            sample_app  |    12.94%  |
  |            sample_sup  |    56.78%  |
  |                sample  |       96%  |
  |------------------------|------------|
  |                 total  |    23.02%  |
  |------------------------|------------|
  coverage calculated from:
    /tmp/workspace/_build/test/cover/ct.coverdata
    /tmp/workspace/_build/test/cover/eunit.coverdata
  cover summary written to: /tmp/workspace/_build/test/cover/index.html

Я хочу извлечь только 23.02, таким образом, число из строка с total. Это регулярное выражение, которое у меня есть до сих пор:

^.+total.+(\d+|\d+\.\d+)%.+$

, но оно не работает, оно соответствует только последнему di git в этой строке.

Я тестирование выкройки на Рубл. .

Ответы [ 3 ]

2 голосов
/ 27 мая 2020

У вас две проблемы. Во-первых, .+ является жадным, что означает, что если он используется для поиска одной строки из файла, он сожрет столько символов, сколько сможет (кроме символов новой строки), но все же обеспечит совпадение, что означает совпадение с последним di git.

Вторая проблема заключается в том, что если вы прочитаете файл в строку и выполните поиск в строке, .* не будет go за первой строкой, потому что он не будет соответствовать символам новой строки. Это можно легко решить, добавив многострочный модификатор (/m), который указывает .* на соответствие всем символам, включая символы новой строки.

Если вы читаете свой файл в строку, вы можете использовать следующие регулярные выражения для извлеките интересующие символы из строки.

r = /
    ^          # match beginning of line
    [ ]*       # match 0+ spaces
    \|         # match a toothpick
    [ ]+       # match 1+ spaces
    total      # match 'total'   
    [ ]+       # match 1+ spaces
    \|         # match a toothpick
    [ ]+       # match 1+ spaces
    \K         # forget everything matched so far
    \d+        # match a digit
    (?:\.\d+)  # match '.' then 1+ digits in non-capture group
    ?          # optionally match the non-capture group
    (?=        # begin a positive lookahead
      %        # match '%'
      [ ]+     # match '%' then 1+ spaces
      \|[ ]*   # match a toothpick then 0+ spaces
      $        # match end-of-line
    )          # end positive lookahead
    /x         # free-spacing mode

Я написал регулярное выражение в режиме свободного интервала 1 , чтобы сделать его самодокументированным. Обычно это записывается следующим образом.

/^ *\| +total +\| +\K\d+(?:\.\d+)?(?=% +\| *$)/

Предположим, вы считываете свой файл в строке, содержащейся в переменной str:

str =<<~END
===> Verifying dependencies...
===> Compiling sample
===> Performing cover analysis...
  |------------------------|------------|
  |                module  |  coverage  |
  |------------------------|------------|
  |            sample_app  |    12.94%  |
  |            sample_sup  |    56.78%  |
  |                sample  |       96%  |
  |------------------------|------------|
  |                 total  |    23.02%  |
  |------------------------|------------|
  coverage calculated from:
    /tmp/workspace/_build/test/cover/ct.coverdata
    /tmp/workspace/_build/test/cover/eunit.coverdata
  cover summary written to: /tmp/workspace/_build/test/cover/index.html
END

Тогда

str[r] #=> "23.02" 

1 В режиме свободного интервала все пробелы удаляются перед анализом регулярного выражения, поэтому пробелы, являющиеся частью регулярного выражения, должны быть защищены. Я сделал это, поместив каждый пробел в класс символов, но вместо этого их можно было бы экранировать или использовать \s (при необходимости).

1 голос
/ 27 мая 2020

Я предпочитаю простоту и использую следующее:

text = <<EOT
===> Verifying dependencies...
===> Compiling sample
===> Performing cover analysis...
  |------------------------|------------|
  |                module  |  coverage  |
  |------------------------|------------|
  |            sample_app  |    12.94%  |
  |            sample_sup  |    56.78%  |
  |                sample  |       96%  |
  |------------------------|------------|
  |                 total  |    23.02%  |
  |------------------------|------------|
  coverage calculated from:
    /tmp/workspace/_build/test/cover/ct.coverdata
    /tmp/workspace/_build/test/cover/eunit.coverdata
  cover summary written to: /tmp/workspace/_build/test/cover/index.html
EOT

text[/ total .+ ([\d.]+)/, 1] # => "23.02"

https://regex101.com/r/SXtXVx/2 демонстрирует и объясняет шаблон.

«total» - это поэтому совпадений подстрок не происходит.

В качестве альтернативы это будет работать:

text.lines.find { |l| l[' total '] }[/[\d.]+/] # => "23.02"

Это работает, позволяя Ruby разбивать текст на массив отдельных строк, находя один содержит total, а затем извлекает число с плавающей запятой.

text.lines # => ["===> Verifying dependencies...\n", "===> Compiling sample\n", "===> Performing cover analysis...\n", "  |------------------------|------------|\n", "  |                module  |  coverage  |\n", "  |------------------------|------------|\n", "  |            sample_app  |    12.94%  |\n", "  |            sample_sup  |    56.78%  |\n", "  |                sample  |       96%  |\n", "  |------------------------|------------|\n", "  |                 total  |    23.02%  |\n", "  |------------------------|------------|\n", "  coverage calculated from:\n", "    /tmp/workspace/_build/test/cover/ct.coverdata\n", "    /tmp/workspace/_build/test/cover/eunit.coverdata\n", "  cover summary written to: /tmp/workspace/_build/test/cover/index.html\n"]
    .find { |l| l['total'] } # => "  |                 total  |    23.02%  |\n"    

В обоих случаях /[\d.]+/] - это все, что нужно для сопоставления числа с плавающей запятой или целого числа:

' 1.2 '[/[\d.]+/] # => "1.2"
' 12  '[/[\d.]+/] # => "12"
0 голосов
/ 27 мая 2020

Вы можете сделать что-то вроде этого:

text = <<~TEXT
===> Verifying dependencies...
===> Compiling sample
===> Performing cover analysis...
  |------------------------|------------|
  |                module  |  coverage  |
  |------------------------|------------|
  |            sample_app  |    12.94%  |
  |            sample_sup  |    56.78%  |
  |                sample  |       96%  |
  |------------------------|------------|
  |                 total  |    23.02%  |
  |------------------------|------------|
  coverage calculated from:
    /tmp/workspace/_build/test/cover/ct.coverdata
    /tmp/workspace/_build/test/cover/eunit.coverdata
  cover summary written to: /tmp/workspace/_build/test/cover/index.html
TEXT

text.lines.find { |line| line.match?(/total/) }[/\d+\.?(\d+)?/].to_f
# => 23.02

Сначала вы конвертируете свою строку в массив, используя String#lines.

Затем вы находите строку с итоговым значением, используя Enumerable#find.

Затем, используя String#[], получите данные соответствия (целые числа или числа с плавающей запятой).

И, наконец, преобразуйте эти данные соответствия в Float.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...