如何搜索多行字符串

时间:2017-05-23 16:51:05

标签: ruby regex

我正在尝试从目录中的rar包中提取文件名。我正在使用返回多行字符串的7z,并希望在输出中搜索“mkv”,“avi”或“srt”文件。

这是我的代码:

ROOT_DIR = "/users/ken/extract"

# Check each directory for Rar packages
# Returns an arary of directories with filenames from the rar's
def checkdirs()
    pkgdirs = {}
    Dir.foreach(ROOT_DIR) do |d|
        if !Dir.glob("#{ROOT_DIR}/#{d}/*.rar").empty?
            rarlist = `7z l #{ROOT_DIR}/#{d}/*.rar`
            puts rarlist  # Returns multilinen output from 7z l
            puts rarlist.scan('*.mkv').first
            pkgdirs[d] = 'filename'
        end
    end
    pkgdirs
end

我可以获得7z输出,但我无法弄清楚如何搜索输出中的字符串。如何搜索输出并返回匹配的行?

这是7z输出的一个例子:

7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=utf8,Utf16=on,HugeFiles=on,64 bits,8 CPUs x64)

Scanning the drive for archives:
1 file, 15000000 bytes (15 MiB)

Listing archive: Gotham.S03E19.HDTV.x264-KILLERS/gotham.s03e19.hdtv.x264-killers.rar

--
Path = Gotham.S03E19.HDTV.x264-KILLERS/gotham.s03e19.hdtv.x264-killers.rar
Type = Rar
Physical Size = 15000000
Total Physical Size = 285988640
Characteristics = Volume FirstVolume VolCRC
Solid = -
Blocks = 1
Multivolume = +
Volume Index = 0
Volumes = 20

   Date      Time    Attr         Size   Compressed  Name
------------------- ----- ------------ ------------  ------------------------
2017-05-23 02:30:52 .....    285986500    285986500  Gotham.S03E19.HDTV.x264-KILLERS.mkv
------------------- ----- ------------ ------------  ------------------------
2017-05-23 02:30:52          285986500    285986500  1 files

------------------- ----- ------------ ------------  ------------------------
2017-05-23 02:30:52          285986500    285986500  1 files

Archives: 1
Volumes: 20
Total archives size: 285988640

我期待这个输出:

 2017-05-23 02:30:52 .....    285986500    285986500  Gotham.S03E19.HDTV.x264-KILLERS.mkv

2 个答案:

答案 0 :(得分:1)

您可以使用:

puts rarlist.scan(/^.*\.mkv/)

正则表达式将从行的开头匹配。

要匹配.mkv.avi.srt,您可以使用:

rarlist.scan(/(^.*\.(mkv|avi|srt))/) {|a,_| puts a}

答案 1 :(得分:0)

解决方案比你制作的解决方案简单得多。

从:

开始
TARGET_EXTENSIONS = %w[mkv avi srt]
TARGET_EXTENSION_RE = /\.(?:#{ Regexp.union(TARGET_EXTENSIONS).source})/
# => /\.(?:mkv|avi|srt)/

output = <<EOT
7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=utf8,Utf16=on,HugeFiles=on,64 bits,8 CPUs x64)

Scanning the drive for archives:
1 file, 15000000 bytes (15 MiB)

Listing archive: Gotham.S03E19.HDTV.x264-KILLERS/gotham.s03e19.hdtv.x264-killers.rar

--
Path = Gotham.S03E19.HDTV.x264-KILLERS/gotham.s03e19.hdtv.x264-killers.rar
Type = Rar
Physical Size = 15000000
Total Physical Size = 285988640
Characteristics = Volume FirstVolume VolCRC
Solid = -
Blocks = 1
Multivolume = +
Volume Index = 0
Volumes = 20

    Date      Time    Attr         Size   Compressed  Name
------------------- ----- ------------ ------------  ------------------------
2017-05-23 02:30:52 .....    285986500    285986500  Gotham.S03E19.HDTV.x264-KILLERS.mkv
------------------- ----- ------------ ------------  ------------------------
2017-05-23 02:30:52          285986500    285986500  1 files

------------------- ----- ------------ ------------  ------------------------
2017-05-23 02:30:52          285986500    285986500  1 files

Archives: 1
Volumes: 20
Total archives size: 285988640
EOT

只需迭代输出中的行和puts匹配:

puts output.lines.grep(TARGET_EXTENSION_RE)

哪个会输出:

2017-05-23 02:30:52 .....    285986500    285986500  Gotham.S03E19.HDTV.x264-KILLERS.mkv

以上是一个基本的解决方案,但根据收到的输出,有些事情可以加速代码:

TARGET_EXTENSIONS = %w[mkv avi srt].map { |e| '.' << e } # => [".mkv", ".avi", ".srt"]
puts output.split(/\r?\n/).select { |l| l.end_with?(*TARGET_EXTENSIONS) }

我必须运行基准测试,但这应该更快,因为如果编写不正确,正则表达式会使代码速度极慢。

你可以尝试:

TARGET_EXTENSION_RE = /\.(?:#{ Regexp.union(TARGET_EXTENSIONS).source})$/
# => /\.(?:mkv|avi|srt)$/
puts output.split(/\r?\n/).grep(TARGET_EXTENSION_RE)

因为锚定模式比未锚定模式快得多。

如果7z档案将生成大量列表(在MB范围内),则最好迭代输入以避免可伸缩性问题。在上面的例子中,output.lines类似于扼杀输出。有关详细信息,请参阅“Why is "slurping" a file not a good practice?”。