Ruby正则表达式:同时捕获和删除的有效方法

Ruby正则表达式:同时捕获和删除的有效方法,ruby,regex,Ruby,Regex,我正在使用ruby中的正则表达式从代码文件中删除注释。代码是C++(但我认为这不是相关的),文件包含了类似的内容: /* Hello! I'm a comment! */ int main(int argc, char* argv[]) { Foo foo; foo.bar(); return 0; } text.scan(UGLY_COMMENTS_REGEX).each do |m| m.method_for_printing_matched_comme

我正在使用ruby中的正则表达式从代码文件中删除注释。代码是C++(但我认为这不是相关的),文件包含了类似的内容:

/*
    Hello! I'm a comment!
*/

int main(int argc, char* argv[])
{
    Foo foo;
    foo.bar();
    return 0;
}
text.scan(UGLY_COMMENTS_REGEX).each do |m|
 m.method_for_printing_matched_comment
 text = text.gsub(m,'');
end 
我的目标是从代码中删除注释,同时解析注释,现在我可以通过捕获然后删除来实现:

text.scan(UGLY_COMMENTS_REGEX).each do |m|
 m.method_for_printing_matched_comment
end 
text = text.gsub(UGLY_COMMENTS_REGEX,'');
我想到的另一种选择是为每个正则表达式匹配执行
gsub
,而不是使用完整的正则表达式,例如:

/*
    Hello! I'm a comment!
*/

int main(int argc, char* argv[])
{
    Foo foo;
    foo.bar();
    return 0;
}
text.scan(UGLY_COMMENTS_REGEX).each do |m|
 m.method_for_printing_matched_comment
 text = text.gsub(m,'');
end 
这个(也是次优的)备选方案的问题是,当匹配包含“组”时,例如m[0],m[1]

由于这样做似乎效率极低,我想知道是否有任何方法可以只进行一次匹配(用于捕获和删除)。

(以及其他
String#gsub
String#sub!
String#sub
)接受可选块(将使用匹配的字符串调用)。所以你可以这样做:

text.gsub!(UGLY_COMMENTS_REGEX) { |m|
  puts m # to print the matched comment  / OR  m.method_for_printing_matched_comment
  ''     # Return value is used as a replacement string; effectively remove the comment
}

我认为以下措施应该有效

代码

def strip_comments(str)
  comments = []
  [str.split(/[ \t]*\/\*|\*\/(?:[ \t]*\n?/)
      .select.with_index {|ar,i| i.even? ? true : (comments << ar.strip; false)}
      .join,
   comments]
end 
str =<<_
/*
    Hello! I'm a comment!
*/

int main(int argc, char* argv[])
{
    Foo foo;
    /* Let's get this one too */
    foo.bar();
    return 0;
}
_

cleaned_code, comments = strip_comments(str)
puts cleaned_code
  # int main(int argc, char* argv[])
  # {
  #    Foo foo;
  #    foo.bar();
  #    return 0;
  # }

puts comments
  # Hello! I'm a comment!
  # Let's get this one too
拆分
/*
*/
上的字符串将创建一个数组,其中每个其他元素都是注释的文本。数组的第一个元素将是要保留的文本,如果字符串以注释开头,则该元素将等于
”。为了保留正确的格式(我希望如此),我还剥离了
/*
前面的任何空格或制表符(但不是换行符),以及
*/
后面的任何制表符或空格,以及换行符

b = str.split(/[ \t]*\/\*|\*\/(?:[ \t]*\n)?/)
  #=> ["",
  #    "\n    Hello! I'm a comment!\n",
  #    "\nint main(int argc, char* argv[])\n{\n    Foo foo;\n",
  #    " Let's get this one too ",
  #    "    foo.bar();\n    return 0;\n}\n"]
我们希望选择非注释的元素,同时保留后者:

enum0 = b.select
  #=> #<Enumerator: [
  #     "",
  #     "\n    Hello! I'm a comment!\n",
  #     "\nint main(int argc, char* argv[])\n{\n    Foo foo;\n",
  #     " Let's get this one too ",
  #     "    foo.bar();\n    return 0;\n}\n"]:select> 
使用
数组#each
执行枚举器及其块:

c = enum1.each {|ar,i| i.even? ? true : (comments << ar.strip; false)}
  #=> ["",
  #    "\nint main(int argc, char* argv[])\n{\n    Foo foo;\n",
  #    "    foo.bar();\n    return 0;\n}\n"]
连接
c
的元素:

cleaned_text = c.join
  #=> "\nint main(int argc, char* argv[])\n{\n    Foo foo;\n    foo.bar();\n    return 0;\n}\n"
并返回:

[cleaned_text, comments]
如上所示

编辑:稍微好一点,我想:

def strip_comments(str)
  a = str.split(/[ \t]*\/\*|\*\/(?:[ \t]*\n)?/)
  a << "" if a.size.odd?
  cleaned, comments = a.each_pair.transpose
  [cleaned.join, comments.map(&:strip)]
end
def条带注释(str)
a=str.split(/[\t]*\/\*\\*\/(?:[\t]*\n)?/)
A.
[cleaned_text, comments]
def strip_comments(str)
  a = str.split(/[ \t]*\/\*|\*\/(?:[ \t]*\n)?/)
  a << "" if a.size.odd?
  cleaned, comments = a.each_pair.transpose
  [cleaned.join, comments.map(&:strip)]
end