我正在尝试编写一个脚本,该脚本将在Ruby文件中的最后一个end
标记之前插入文本。例如,我想插入以下内容:
def hello
puts "hello!"
end
在以下文件中,就在课程结束之前:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
helper_method :authenticated?, :current_user
def current_user?
session[:current_user]
end
end
结果应如下所示:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
helper_method :authenticated?, :current_user
def current_user?
session[:current_user]
end
def hello
puts "hello!"
end
end
我试图找到一个与end
的最后一次出现匹配的正则表达式,并将其替换为我要添加的块,但我尝试过的所有正则表达式仅匹配第一个end
。试过这些:
end(?=[^end]*$)
end(?!.*end)
(.*)(end)(.*)
要替换字符串,我会执行以下操作(也许EOL字符会搞砸匹配?):
file_to_override = File.read("app/controllers/application_controller.rb")
file_to_override = file_to_override.sub(/end(?=[^end]*$)/, "#{new_string}\nend")
编辑:我也尝试使用How to replace the last occurrence of a substring in ruby?中提供的解决方案,但奇怪的是,它取代了 end
的所有出现。
我做错了什么?谢谢!
答案 0 :(得分:2)
这篇文章中解释的方法也在这里工作。您只需重新组织捕获组并使用/m
修饰符强制.
匹配换行符号。
new_string = <<EOS
def hello
puts "Hello!"
end
EOS
file_to_override = <<EOS
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
helper_method :authenticated?, :current_user
def current_user?
session[:current_user]
end
end
EOS
file_to_override=file_to_override.gsub(/(.*)(\nend\b.*)/m, "\\1\n#{new_string}\\2")
puts file_to_override
请参阅IDEONE demo
/(.*)(\nend\b.*)/m
模式将匹配并捕获到第1组所有文本直到最后一个整个单词(由于\n
之前和\b
之后)end
之前使用换行符,并将换行符“结束”以及剩余的任何内容放入第2组。在替换中,我们使用后向引用 \1
和{{对已捕获的子字符串进行反向引用1}}并插入我们需要插入的字符串。
如果在上一个\2
之后没有其他字词,您甚至可以使用end
正则表达式。
答案 1 :(得分:1)
假设您将文件读入字符串text
:
text = <<_
class A
def a
'hi'
end
end
_
并希望插入字符串to_enter
:
to_enter = <<_
def hello
puts "hello!"
end
_
在最后end
之前。你可以写
r = /
.* # match any number of any character (greedily)
\K # discard everything matched so far
(?=\n\s*end\b) # match end-of-line, indenting spaces, and "end" followed
# by a word break in a positive lookahead
/mx # multi-line and extended/free-spacing regex definition modes
puts text.sub(r, to_enter)
(prints)
class A
def a
'hi'
end
def hello
puts "hello!"
end
end
请注意,sub
正在用to_enter
替换空字符串。
答案 2 :(得分:0)
编辑:来自Wiktor的回答正是我所寻求的。留下以下因为它也有效。
最后,我放弃了使用正则表达式替换。相反,我使用最后end
的位置:
positions = file_to_override.enum_for(:scan, /end/).map { Regexp.last_match.begin(0) }
然后,在写入文件之前,我在字符串的最后位置添加了我需要的内容 - 1:
new_string = <<EOS
def hello
puts "Hello!"
end
EOS
file_to_override[positions.last - 1] = "\n#{test_string}\n"
File.open("app/controllers/application_controller.rb", 'w') {|file| file.write(file_to_override)}
这有效,但对我来说它看起来不像惯用的Ruby。
答案 3 :(得分:0)
您还可以找到并替换“end”的最后一次出现(请注意,这也会与# Hello my friend
中的结尾相匹配,但请参见下文)
# Our basics: In this text ...
original_content = "# myfile.rb\n"\
"module MyApp\n"\
" class MyFile\n"\
" def myfunc\n"\
" end\n"\
" end\n"\
"end\n"
# ...we want to inject this:
substitute = "# this will come to a final end!\n"\
"end\n"
# Now find the last end ...
idx = original_content.rindex("end") # => index of last "end"(69)
# ... and substitute it
original_content[idx..idx+3] = substitute # (3 = "end".length)
这个解决方案有点老了(处理字符串中的索引在几年前感觉更冷)并且在这种形式下更“脆弱”,但避免你坐下来消化正则表达式。不要误会我的意思,正则表达式是一种令人难以置信的力量的工具,学习它们的时间是值得的。
也就是说,您可以使用rindex
(例如rindex(/ *end/)
)来使用其他答案中的所有正则表达式。