At the risk of stating the obvious, you are most likely never satisfying scanner.eos?
, which in turn would mean that you're not advancing the scan pointer to the end of the string. Since the only change to scanner.pos
in the else
branch of parse!
is to decrement it (i.e. by len
), this would be understandable. If the if
branch doesn't advance it to the end, you'll never terminate.
Ruby: Using StringScanner causes infinite loop
-
23-09-2022 - |
题
I have the following class:
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
The above class parses files that are setup like so:
[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.
You start running it like this:
require 'conf_parser'
cf = ConfParser.read "/path/to/conf/file"
For some reason when the parse!
method runs, an infinite loop occurs and I can't figure out why. Any reason why this would be happening? I have never used StringScanner before, so it may be my lack of knowledge of the class
解决方案
不隶属于 StackOverflow