Ruby:使用StringScanner会导致无限循环

时间:2013-12-31 01:30:18

标签: ruby regex fileparsing

我有以下课程:

require 'strscan'

class ConfParser
  include Enumerable

  class Error < StandardError; end
  VERSION = '0.0.1'
  SECTION_REGEX = /^\[       # Opening bracket
                   ([^\]]+)  # Section name
                   \]$       # Closing bracket
                 /x
  PARAMETER_REGEX = /^\s*([^:]+)  # Option
                      :
                      (.*?)$      # Value
                    /x

  attr_accessor :filename, :sections

  CONFIG_DIRECTORY = "./config"
  ENCODING = "UTF-8"

  def self.read(filename, opts = {})
    new(opts.merge(:filename => filename))
  end

  def initialize(opts = {})
    @filename = opts.fetch(:filename)
    @separator = opts.fetch(:separator, ":")
    @file = "#{CONFIG_DIRECTORY}/#{@filename}"
    @content = nil
    @config = Hash.new { |h,k| h[k] = Hash.new }

    load
  end

  def load
    raise_error("First line of config file contain be blank") if first_line_empty?

    f = File.open(@file, 'r')
    @content = f.read
    parse!

    ensure
      f.close if f && !f.closed?
  end

  def sections
    @config.keys
  end

  def [](section)
    return nil if section.nil?

    @config[section.to_s]
  end

  def []=( section, value )
    @config[section.to_s] = value
  end

  private

    def parse!
      @_section = nil
      @_current_line = nil
      property = ''
      string = ''

      @config.clear

      scanner = StringScanner.new(@content)

      until scanner.eos?
        @_current_line = scanner.check(%r/\A.*$/) if scanner.bol?

        if scanner.scan(SECTION_REGEX)
          @_section = @config[scanner[1]]
        else
          tmp = scanner.scan_until(%r/([\n"#{@param}#{@comment}] | \z | \\[\[\]#{@param}#{@comment}"])/mx)
          raise_error if tmp.nil?

          len = scanner[1].length
          tmp.slice!(tmp.length - len, len)

          scanner.pos = scanner.pos - len
          string << tmp
        end
      end

      process_property(property, string)

      logger @config
    end

    def process_property( property, value )
      value.chomp!
      return if property.empty? and value.empty?
      return if value.sub!(%r/\\\s*\z/, '')

      property.strip!
      value.strip!

      parse_error if property.empty?

      current_section[property.dup] = unescape_value(value.dup)

      property.slice!(0, property.length)
      value.slice!(0, value.length)

      nil
    end

    def logger log
      puts "*"*50
      puts log
      puts "*"*50
    end

    def first_line_empty?
      File.readlines(@file).first.chomp.empty?
    end

    def raise_error(msg = 'Error processing line')
      raise Error, "#{msg}: #{@_current_line}"
    end

    def current_section
      @_section ||= @config['header']
    end

end

上面的类解析了如下设置的文件:

[header]
project: Hello World
budget : 4.5
accessed :205

[meta data]
description : This is a tediously long description of the Hello World
  project that you are taking. Tedious isn't the right word, but
  it's the first word that comes to mind.

correction text: I meant 'moderately,' not 'tediously,' above.

[ trailer ]
budget:all out of budget.

你开始像这样运行:

require 'conf_parser'
cf = ConfParser.read "/path/to/conf/file"

出于某种原因,当parse!方法运行时,会发生无限循环,我无法弄清楚原因。有什么理由会发生这种情况吗?我之前从未使用过StringScanner,所以可能是我缺乏对类的了解

1 个答案:

答案 0 :(得分:1)

冒着明显的风险,你很可能永远不会满足scanner.eos?,这反过来意味着你没有将扫描指针推进到字符串的末尾。由于scanner.pos的{​​{1}}分支中else的唯一更改是减少(即parse!),这是可以理解的。如果len分支没有将其推进到最后,您将永远不会终止。