读者可能正在寻找一个优雅的,至少不是骇人听闻的解决方案来解决这个问题。这也是我的目标,但是,唉,这是我能想到的最好的。
代码
def convert(str)
subs = []
str.gsub(/"[^"]*"| *\| */) do |s|
if s.match?(/ *\| */)
'|'
else
subs << s
'*'
end
end.gsub(/ +/, ' AND ').
gsub(/[*|]/) { |s| s == '|' ? ' OR ' : subs.shift }
end
例子
puts convert(%Q{(A | B) "C D" (E | "F G" | "H I J") ("K L" ("M N" | "O P")) Q R})
#-> (A OR B) AND "C D" AND (E OR "F G" OR "H I J") AND ("K L" AND ("M N" OR "O P")) AND Q AND R
puts convert(%Q{(A|B) C D (E| "F G" |"H I J") ("K L" ("M N" | "O P")) Q R})
#-> (A OR B) AND C AND D AND (E OR "F G" OR "H I J") AND ("K L" AND ("M N" OR "O P")) AND Q AND R
请注意,在此示例中,某些管道之前和/或之后没有空格,并且在双引号字符串之外的某些地方有多个空格。
puts convert(%Q{(Ant | Bat) Cat Dog (Emu | "Frog Gorilla" | "Hen Ibex Jackel") ("Khawla Lynx" ("Magpie Newt" | "Ocelot Penguin")) Quail Rabbit})
#-> (Ant OR Bat) AND Cat AND Dog AND (Emu OR "Frog Gorilla" OR "Hen Ibex Jackel") AND ("Khawla Lynx" AND ("Magpie Newt" OR "Ocelot Penguin")) AND Quail AND Rabbit
在这里,我用单词替换了大写字母。
解释
要看看这是如何工作的,让
str = %Q{(A | B) "C D" (E | "F G" | "H I J") ("K L" ("M N" | "O P")) Q R}
#=> "(A | B) \"C D\" (E | \"F G\" | \"H I J\") (\"K L\" (\"M N\" | \"O P\")) Q R"
然后
subs = []
str.gsub(/"[^"]*"| *\| */) do |s|
if s.match?(/ *\| */)
'|'
else
subs << s
'*'
end
end
#=> "(A|B) * (E|*|*) (* (*|*)) Q R"
subs
#=> ["\"C D\"", "\"F G\"", "\"H I J\"", "\"K L\"", "\"M N\"", "\"O P\""]
如您所见,我删除了管道周围的空格并将所有带引号的字符串替换为星号,将这些字符串保存在数组subs
中,以便以后可以将星号替换为其原始值。星号的选择当然是任意的。
正则表达式读作“匹配零个或多个字符的双引号字符串或'|'
可选地前面和/或后面跟着空格的管道 ( )”。
由于这些替换,所有剩余的空格字符串都将替换为' AND '
:
s2 = s1.gsub(' +', ' AND ')
#=> "(A|B) AND * AND (E|*|*) AND (* AND (*|*)) AND Q AND R"
它仍然用原始值替换每个星号'|'
:' OR '
s2.gsub(/[*|]/) { |s| s == '|' ? ' OR ' : subs.shift }
#=> "(A OR B) AND \"C D\" AND (E OR \"F G\" OR \"H I J\") AND (\"K L\" AND (\"M N\" OR \"O P\")) AND Q AND R"