22

这是Ruby 1.8.7但应该与 1.9.x 相同

我正在尝试拆分字符串,例如:

a = "foo.bar.size.split('.').last"
# trying to split into ["foo", "bar","split('.')","last"]

基本上将其拆分为它所代表的命令,我正在尝试使用 Regexp 但不确定如何,想法是使用 regexp

a.split(/[a-z\(\)](\.)[a-z\(\)]/)

这里尝试使用 group(\.)来拆分它,但这似乎不是一个好方法。

4

4 回答 4

35

我认为这会做到:

a.split(/\.(?=[\w])/)

我不知道您对正则表达式了解多少,但这(?=[\w])是一个前瞻,它说“如果下一个字符是字母类型的字符,则仅匹配点”。前瞻实际上不会抓取它匹配的文本。它只是“看起来”。所以结果正是您正在寻找的:

> a.split(/\.(?=[\w])/)
 => ["foo", "bar", "size", "split('.')", "last"] 
于 2012-10-11T12:37:16.607 回答
8

恐怕正则表达式不会带你走太远。例如,考虑以下表达式(它们也是有效的 Ruby)

"(foo.bar.size.split( '.' )).last"
"(foo.bar.size.split '.').last"
"(foo.bar.size.split '( . ) . .(). .').last"

问题是,调用列表实际上是调用树。最简单的解决方案可能是使用 Ruby 解析器并根据您的需要转换解析树(在此示例中,我们递归地下降到调用树,将调用收集到一个列表中):

# gem install ruby_parser
# gem install awesome_print
require 'ruby_parser'
require 'ap'

def calls_as_list code
    tree = RubyParser.new.parse(code)

    t = tree
    calls = []

    while t
        # gather arguments if present
        args = nil
        if t[3][0] == :arglist
            args = t[3][1..-1].to_a
        end
        # append all information to our list
        calls << [t[2].to_s, args]
        # descend to next call
        t = t[1]
    end

    calls.reverse
end

p calls_as_list "foo.bar.size.split('.').last"
#=> [["foo", []], ["bar", []], ["size", []], ["split", [[:str, "."]]], ["last", []]]
p calls_as_list "puts 3, 4"
#=> [["puts", [[:lit, 3], [:lit, 4]]]]

并显示任何输入的解析树:

ap RubyParser.new.parse("puts 3, 4")
于 2012-10-11T13:08:20.710 回答
4
a = "foo.bar.size.split('.').last"
p a.split(/(?<!')\.(?!')/)

#=> ["foo", "bar", "size", "split('.')", "last"]

您正在寻找 Lookahead 和 Lookbehind 断言。 http://www.regular-expressions.info/lookaround.html

于 2012-10-11T15:04:01.633 回答
2

这里我没有 ruby​​ 环境。我尝试使用 python re.split()。

In : re.split("(?<!')\.(?!')",a)
Out: ['foo', 'bar', 'size', "split('.')", 'last']

上面的正则表达式有负前瞻后瞻,以确保只有单引号之间的“点”不能用作分隔符。

当然,对于您给出的示例,lookbehind 或lookahead 之一就足够了。您可以根据自己的要求选择正确的方式。

于 2012-10-11T12:48:09.467 回答