2010-07-06 94 views
1

我使用Ruby创建了一些基本的工作辅助实用程序。我遇到了一个我并不需要解决的问题,但好奇心对我来说是最好的。使用Ruby查找字符串的第一个匹配项

我希望能够做的是搜索一个文件的内容,从一个特定的行开始,找到第一个出现的字符串。

举例来说,如果我有保存在文件中下面的文字,我想能够搜索“CREATE PROCEDURE”开始在4号线有这样的回报/输出“CREATE PROCEDURE sp_MERGE_TABLE”

CREATE PROCEDURE sp_MERGE_TABLE 
AS 
SOME HORRIBLE STATEMENT 
HERE 

CREATE PROCEDURE sp_SOMETHING_ELSE 
AS 
A DIFFERENT STATEMENT 
HERE 

寻找内容不是一个挑战,但指定一个起点 - 不知道。然后向后搜索...以及...

任何帮助在所有赞赏!

TIA!

+0

你有没有尝试过这个问题呢?如果是这样,任何代码或任何伪代码作为起点?否则,我们基本上为你做了很多。没有冒犯任何想法! – Kezzer 2010-07-06 13:08:41

+0

通常'\ n'是一个新的行字符。您可以计数字符以指定该行。如果在文件中找到第二个'\ n',那么它后面的字符就是第3行的第一个字符。 – 2010-07-06 13:10:49

+0

没有冒犯 - 我当然不希望你们为我做这项工作,就像我之前说过的 - 我这样做的成本可能超过了好处。 这实际上是一个较大努力的一小部分,我用一些不同的方法(从TSQL脚本到grep组合)对它进行了轻扫。 我非常喜欢Ruby,并为此接下来。我比任何事情都更好奇。我曾考虑将文件加载到数组中,然后向后迭代,但似乎缺乏在Ruby解决方案中经常遇到的优雅。 我的企图很难看 - 我希望有一位公主。 – 2010-07-06 13:35:45

回答

0

编辑:

我刚刚有了一个更好的主意,但我还是要去,包括旧的解决方案。

反向搜索的好处意味着您只需要读取文件的第一个块,直到指定的行号。对于接近,你越来越接近start_line,如果你发现一个匹配,你只是忘了旧的一个..你仍然在一些冗余数据的开始读入,但至少它是O(n)

path = "path/to/file" 
start_line = 20 
search_string = "findme!" 

#assuming file is at least start_line lines long 
match_index = nil 
f = File.new(path) 
start_line.times do |i| 
    line = f.readline 
    match_index = i if line.include? search_string 
end 

puts "Matched #{search_string} on line #{match_index}" 

当然,请记住这个文件的大小在回答你的问题中起着重要的作用。

如果你想真的认真,你可以看看IO类 - 看起来这可能是最终的解决方案。未经测试,只是一个想法。

f = File.new(path) 
start_line.downto(0) do |i| 
    f.lineno = i 
    break if f.gets.include?(search_string) 
end 

原文:

对于一个详尽的解决方案,你可以尝试像下面这样。缺点是你需要将整个文件读入内存,但是如果它没有匹配到达顶部,它会考虑自下而上的情况。未经测试。

path = "path/to/file" 
start_line = 20 
search_string = "findme!" 

#get lines of the file into an array (chomp optional) 
lines = File.readlines(path).map(&:chomp) 

#"cut" the deck, as with playing cards, so start_line is first in the array 
lines = lines.slice!(start_line..lines.length) + lines 

#searching backwards can just be searching a reversed array forwards 
lines.reverse! 

#search through the reversed-array, for the first occurence 
reverse_occurence = nil 
lines.each_with_index do |line,index| 
    if line.include?(search_string) 
    reverse_occurence = index 
    break 
    end 
end 

#reverse_occurence is now either "nil" for no match, or a reversed-index 
#also un-cut the array when calculating the index 
if reverse_occurence 
    occurence = lines.size - reverse_occurence - 1 + start_line 
    line = lines[reverse_occurence] 
    puts "Matched #{search_string} on line #{occurence}" 
    puts line 
end 
+0

BAM !!!我喜欢它!您的原始解决方案就是我的想法首先出现的地方 - 您的新改进解决方案就是我想让自己的思想走向的地方。它看起来像没有任何类型的文件/字符串查找调用,可以修改,以满足我的要求。就我而言,这个答案是下一个最好的东西。 非常感谢所有的帮助! – 2010-07-06 13:59:22

+0

再次更新了答案 - 看起来您可以执行诸如“File.lineno = my_line_number”之类的操作。另外'seek'和'read'可能是你的朋友,但它可能是过度杀伤,除非你的文件是巨大的;) – Jeriko 2010-07-06 14:09:13

1

我认为你必须按行读入文件一行

然后follwing将工作

flag=true 
    if flag && line.include?("CREATE PROCEDURE") 
    puts line 
    flag=false 
    end 
+0

我很欣赏这个尝试,并且我想这样做。但我实际上必须从指定的起始行逐行读取文件行。这意味着将整个事物加载到一个数组中,然后向后走。可以,但丑陋。如上所述,我真的只是想通过这个学习一些更优雅的文件操作方法。 谢谢你的尝试! – 2010-07-06 13:37:33

1

如果性能是不是一个大问题,你可以只使用一个简单的循环:

# pseudocode 
line_no = 0 
while line_no < start_line 
    read line from file 
    if content_found in this line 
    last_seen = line_no # or file offset 
    end 
    line_no += 1 
end 
return last_seen 

恐怕你将不得不逐行通过文件,除非你有一些索引,指向行的开头。这会让循环变得更简单一些,但是以反向方式处理文件更加困难(除非将整个文件保存在内存中)。

0

1)将整个文件读入一个字符串。
2)反转文件数据字符串。
3)反转搜索字符串。
4)向前搜索。请记住匹配行尾而不是行首,并从位置末尾减去N开始,而不是从N开始。

不是非常快速或高效,但它很优雅。或者至少是聪明的。

相关问题