繁体   English   中英

Ruby-Regex多行

[英]Ruby - Regex Multiple lines

我正在寻找对某些文件的搜索,以查看它们是否在文件顶部有注释。

这是我要搜索的内容:

#++
#    app_name/dir/dir/filename
#    $Id$
#--

我将其作为REGEX,结果很简短:

:doc => { :test => '^#--\s+[filename]\s+\$Id'
if @file_text =~ Regexp.new(@rules[rule][:test])
....

有什么建议么?

检查以下示例:

string = <<EOF
#++
##    app_name/dir/dir/filename
##    $Id$
##--

foo bar
EOF

puts /#\+\+.*\n##.*\n##.*\n##--/.match(string)

模式匹配以##开头的两行,以#++开头并以#--结尾的两行,并将这些边界包括在匹配项中。 如果我的问题正确,那应该就是您想要的。

您可以使用以下模式来概括模式,以匹配第一个#++和第一个 #--之间的所有内容(包括它们):

puts /#\+\+.*?##--/m.match(string)

与其尝试以单一模式进行所有操作,否则随着文件头的更改/增长而变得难以维护,而是使用多个小测试来提供粒度。 我会做类似的事情:

lines = '#++
#    app_name/dir/dir/filename
#    $Id$
#--
'

拆分文本,以便您可以检索所需的行并对其进行规范化:

l1, l2, l3, l4 = lines.split("\n").map{ |s| s.strip.squeeze(' ') }

它们现在包含的内容是:

[l1, l2, l3, l4] # => ["#++", "# app_name/dir/dir/filename", "# $Id$", "#--"]

这是一组测试,每行一个:

!!(l1[/^#\+\+/] && l2[/^#\s[\w\/]+/] && l3[/^#\s\$Id\$/i] && l4[/^#--/]) # => true

这是正在测试的内容以及每个返回的内容:

l1[/^#\+\+/] # => "#++"
l2[/^#\s[\w\/]+/] # => "# app_name/dir/dir/filename"
l3[/^#\s\$Id\$/i] # => "# $Id$"
l4[/^#--/] # => "#--"

抓取文件的前“ n”行有很多不同的方法。 这里是一些:

File.foreach('test.txt').to_a[0, 4] # => ["#++\n", "#    app_name/dir/dir/filename\n", "#    $Id$\n", "#--\n"]
File.readlines('test.txt')[0, 4]    # => ["#++\n", "#    app_name/dir/dir/filename\n", "#    $Id$\n", "#--\n"]
File.read('test.txt').split("\n")[0, 4] # => ["#++", "#    app_name/dir/dir/filename", "#    $Id$", "#--"]

这些的缺点是它们都“吸取”输入文件,在一个很大的文件上会引起问题。 编写一段打开文件,读取前四行并将它们返回到数组中的代码很简单。 这未经测试,但看起来正确:

def get_four_lines(path)

  ary = []

  File.open(path, 'r') do |fi|
    4.times do
      ary << fi.readline
    end
  end

  ary

end

以下是一个简短的基准测试,用以说明我为什么要这样做:

require 'fruity'

def slurp_file(path)
  File.read(path).split("\n")[0,4] rescue []
end

def read_first_four_from_file(path)
  ary = []

  File.open(path, 'r') do |fi|
    4.times do
      ary << fi.readline
    end
  end
  ary
rescue
  []
end

PATH = '/etc/'
FILES = Dir.entries(PATH).reject{ |f| f[/^\./] || Dir.exist?(f) }.map{ |f| File.join(PATH, f) }

compare do
  slurp {
    FILES.each do |f|
      slurp_file(f)
    end
  }

  read_four {
    FILES.each do |f|
      read_first_four_from_file(f)
    end
  }
end

作为根输出运行:

Running each test once. Test will take about 1 second.
read_four is faster than slurp by 2x ± 1.0

那正在我的/ etc目录中读取大约105个文件。

修改测试以实际解析行并测试返回true / false:

require 'fruity'

def slurp_file(path)
  ary = File.read(path).split("\n")[0,4] 
  !!(/#\+\+\n(.|\n)*?##\-\-/.match(ary.join("\n")))
rescue
  false # return a consistent value to fruity
end

def read_first_four_from_file(path)
  ary = []

  File.open(path, 'r') do |fi|
    4.times do
      ary << fi.readline
    end
  end
  l1, l2, l3, l4 = ary
  !!(l1[/^#\+\+/] && l2[/^#\s[\w\/]+/] && l3[/^#\s\$Id\$/i] && l4[/^#--/])
rescue
  false # return a consistent value to fruity
end

PATH = '/etc/'
FILES = Dir.entries(PATH).reject{ |f| f[/^\./] || Dir.exist?(f) }.map{ |f| File.join(PATH, f) }

compare do
  slurp {
    FILES.each do |f|
      slurp_file(f)
    end
  }

  read_four {
    FILES.each do |f|
      read_first_four_from_file(f)
    end
  }
end

再次运行将返回:

Running each test once. Test will take about 1 second.
read_four is faster than slurp by 2x ± 1.0

您的基准测试不公平。

这是“公平”的:

require 'fruity'

def slurp_file(path)
  text = File.read(path)
  !!(/#\+\+\n(.|\n)*?##\-\-/.match(text))
rescue
  false # return a consistent value to fruity
end

def read_first_four_from_file(path)
  ary = []

  File.open(path, 'r') do |fi|
    4.times do
      ary << fi.readline
    end
  end
  l1, l2, l3, l4 = ary
  !!(l1[/^#\+\+/] && l2[/^#\s[\w\/]+/] && l3[/^#\s\$Id\$/i] && l4[/^#--/])
rescue
  false # return a consistent value to fruity
end

PATH = '/etc/'
FILES = Dir.entries(PATH).reject{ |f| f[/^\./] || Dir.exist?(f) }.map{ |f| File.join(PATH, f) }

compare do
  slurp {
    FILES.each do |f|
      slurp_file(f)
    end
  }

  read_four {
    FILES.each do |f|
      read_first_four_from_file(f)
    end
  }
end

哪个输出:

Running each test once. Test will take about 1 second.
read_four is similar to slurp

在进行匹配之前,将拆分后的字符串重新连接成更长的字符串是错误的路径,因此从完整文件的内容进行工作是一项更均匀的测试。

[...]只需阅读前四行并应用模式,就是这样

不仅如此。 编写用于查找跨越多行信息的多行正则表达式不能传递单个文本行并返回准确的结果,因此它需要获得一个长字符串。 确定多少字符组成四行行只会增加开销,并使算法变慢; 这就是以前的基准测试所做的,并且不是“公平的”。

取决于您的输入数据。 如果您在完整(更大)的源代码文件夹上运行此代码,则它将大大降低其运行速度。

目录中有105多个文件。 这是一个相当大的文件数量,但是对大量文件进行迭代不会显示出差异,因为Ruby的打开文件能力不是问题,这是一次读取文件的I / O速度与line-按行。 而且,根据经验,我知道逐行I / O很快。 同样,基准测试表明:

require 'fruity'

LITTLEFILE = 'little.txt'
MEDIUMFILE = 'medium.txt'
BIGFILE = 'big.txt'

LINES = '#++
#    app_name/dir/dir/filename
#    $Id$
#--
'

LITTLEFILE_MULTIPLIER = 1
MEDIUMFILE_MULTIPLIER = 1_000
BIGFILE_MULTIPLIER = 100_000

File.write(BIGFILE, LINES * BIGFILE_MULTIPLIER)

def _slurp_file(path)
  File.read(path)
  true # return a consistent value to fruity
end

def _read_first_four_from_file(path)
  ary = []

  File.open(path, 'r') do |fi|
    4.times do
      ary << fi.readline
    end
  end
  l1, l2, l3, l4 = ary
  true # return a consistent value to fruity
end

[
  [LITTLEFILE, LITTLEFILE_MULTIPLIER],
  [MEDIUMFILE, MEDIUMFILE_MULTIPLIER],
  [BIGFILE,    BIGFILE_MULTIPLIER]
].each do |file, mult|

  File.write(file, LINES * mult)
  puts "Benchmarking against #{ file }"
  puts "%s is %d bytes" % [ file, File.size(file)]

  compare do
    slurp                     { _slurp_file(file)                }
    read_first_four_from_file { _read_first_four_from_file(file) }
  end

  puts
end

随着输出:

Benchmarking against little.txt
little.txt is 49 bytes
Running each test 128 times. Test will take about 1 second.
slurp is similar to read_first_four_from_file

Benchmarking against medium.txt
medium.txt is 49000 bytes
Running each test 128 times. Test will take about 1 second.
read_first_four_from_file is faster than slurp by 39.99999999999999% ± 10.0%

Benchmarking against big.txt
big.txt is 4900000 bytes
Running each test 128 times. Test will take about 4 seconds.
read_first_four_from_file is faster than slurp by 100x ± 10.0

读取一个四行的小文件, read速度与foreach一样快,但是一旦文件大小增加,读取整个文件的开销就会开始影响时间。

众所周知,依靠文件抓取的任何解决方案都是一件坏事。 它不可扩展,如果遇到BIG文件,实际上可能由于内存分配而导致代码暂停。 读取前四行将始终以与文件大小无关的一致速度运行,因此,每次使用该技术时,文件大小都有可能会有所不同。 或者,至少,要非常注意运行时间的影响以及文件提取可能引起的潜在问题。

您可能需要尝试以下方法: \\#\\+{2}(?:.|[\\r\\n])*?\\#\\-{2}

正则表达式可视化

工作演示@ regex101

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM