Как вернуть одинаковый результат из str.scan и str.match в Ruby с помощью регулярных выражений - PullRequest
0 голосов
/ 30 апреля 2020

У меня есть метод для захвата расширения как группы с использованием регулярного выражения:

def test(str)

  word_match = str.match(/\.(\w*)/)

  word_scan = str.scan(/\.(\w*)/)

  puts word_match, word_scan

end

test("test.rb")

Таким образом, он вернет:

.rb
rb

Почему я получил бы другой ответ?

Ответы [ 2 ]

3 голосов
/ 30 апреля 2020

Причина в том, что match и scan возвращают разные объекты. match возвращает либо MatchData объект, либо String, а scan возвращает Array. Вы можете увидеть это, вызвав метод class для ваших переменных

puts word_match.class # => MatchData
puts word_scan.class  # => Array

Если вы посмотрите на метод to_s в MatchData, вы заметите, что он возвращает всю совпавшую строку, скорее чем захватывает. Если вам нужны только захваты, вы можете использовать метод captures.

puts word_match.captures # => "rb"
puts word_match.captures.class # => Array

Если вы передадите блок методу match, вы получите строку обратно с результатами, аналогичными scan method.

word_match = str.match(/\.(\w*)/) { |m| m.captures } # => [["rb"]]
puts word_scan.inspect  #=> ["rb"]
puts word_match #=> "rb

Более подробную информацию об этих методах и о том, как они работают, можно найти в ruby -do c для класса String.

2 голосов
/ 30 апреля 2020

Не пишите свой собственный код для этого, используйте собственный встроенный код Ruby:

File.extname("test.rb")         # => ".rb"
File.extname("a/b/d/test.rb")   # => ".rb"
File.extname(".a/b/d/test.rb")  # => ".rb"
File.extname("foo.")            # => "."
File.extname("test")            # => ""
File.extname(".profile")        # => ""
File.extname(".profile.sh")     # => ".sh"

В некоторых случаях вы пропускаете. Сравните вышеприведенное с результатами ваших попыток:

fnames = %w[
  test.rb
  a/b/d/test.rb
  .a/b/d/test.rb
  foo.
  test
  .profile
  .profile.sh
]

fnames.map { |fn|
  fn.match(/\.(\w*)/).to_s 
}
# => [".rb", ".rb", ".a", ".", "", ".profile", ".profile"]

fnames.map { |fn|
  fn.scan(/\.(\w*)/).to_s  
}
# => ["[[\"rb\"]]",
#     "[[\"rb\"]]",
#     "[[\"a\"], [\"rb\"]]",
#     "[[\"\"]]",
#     "[]",
#     "[[\"profile\"]]",
#     "[[\"profile\"], [\"sh\"]]"]

В документации для File.extname написано:

Возвращает расширение (часть файла имя в path, начиная с последнего периода).

Если path является точечным файлом или начинается с периода, то начальная точка не имеет отношения к началу расширения.

Пустая строка также будет возвращена, когда точка является последним символом в path.

В Windows, конечные точки усекаются.

Класс File имеет много других полезных методов для разделения имен файлов. Также есть класс Pathname , который очень полезен для подобных вещей.

...