代码之家  ›  专栏  ›  技术社区  ›  Simon

如何从ruby中取消scape c风格的转义序列?

  •  6
  • Simon  · 技术社区  · 14 年前

    在ruby中,如何解码c风格的转义序列?例如,'\n'到换行符,'\t'到制表符?

    3 回复  |  直到 14 年前
        1
  •  11
  •   GreyCat    14 年前

    好吧,如果你不喜欢 eval 解决方案是,我在Ruby中入侵了一个简单的状态机来正确解析字符串中的简单“\n”和“\t”,包括反斜杠本身的预转义。这里是:

    BACKSLASH = "\\"
    
    def unescape_c_string(s)
        state = 0
        res = ''
        s.each_char { |c|
            case state
            when 0
                case c
                when BACKSLASH then state = 1
                else res << c
                end
            when 1
                case c
                when 'n' then res << "\n"; state = 0
                when 't' then res << "\t"; state = 0
                when BACKSLASH then res << BACKSLASH; state = 0
                else res << BACKSLASH; res << c; state = 0
                end
            end
        }
        return res
    end
    

    这个可以很容易地扩展以支持更多的字符,包括多字符实体,比如 \123 . 证明其工作的测试单元:

    require 'test/unit'
    
    class TestEscapeCString < Test::Unit::TestCase
        def test_1
            assert_equal("abc\nasd", unescape_c_string('abc\nasd'))
        end
        def test_2
            assert_equal("abc\tasd", unescape_c_string('abc\tasd'))
        end
        def test_3
            assert_equal("abc\\asd", unescape_c_string('abc' + BACKSLASH * 2 + 'asd'))
        end
        def test_4
            assert_equal("abc\\nasd", unescape_c_string('abc' + BACKSLASH * 2 + 'nasd'))
        end
        def test_5
            assert_equal("abc\\\nasd", unescape_c_string('abc' + BACKSLASH * 3 + 'nasd'))
        end
        def test_6
            assert_equal("abc\\\\nasd", unescape_c_string('abc' + BACKSLASH * 4 + 'nasd'))
        end
    end
    
        2
  •  3
  •   GreyCat    14 年前

    更短,甚至更黑,相当危险,由于评估:

    eval "\"#{string}\""
    一个简单的例子:
    > a = '1\t2\n3'
    > puts a
    1\t2\n3
    > puts eval "\"#{a}\""
    1       2
    3
    

        3
  •  0
  •   Jörg W Mittag    14 年前

    编辑:请注意,这实际上不起作用。您真的需要在这里用状态机来构建一个正确的解析器,它跟踪您是否处于转义序列中。


    Ruby支持许多相同的转义序列,因此您可以构建这样一个简单的转换表:

    T = {
      '\n' => "\n",
      '\t' => "\t",
      '\r' => "\r"
    }
    

    然后使用转换表替换源字符串中的序列:

    a = '1\t2\n3'
    
    a.gsub(/#{T.keys.map(&Regexp.method(:escape)).join('|')}/, &T.method(:[]))
    # => "1\t2\n3"