Edit 2: Вот краткое, общее решение, основанное по редактируемому вопросу:
# Replace %foo in format string with value of calling obj.foo
def custom_format(format, obj)
format.gsub(/%([a-z]\w*)/i){ |s| obj.respond_to?($1) ? obj.send($1) : s }
end
formatter = "DATA OBJECT %id
%name
Created on %date
See %url for more %infoz
We are %pct% done."
# Create a simple class with accessor methods for these attributes
DataObject = Struct.new(:id,:name,:date,:url,:pct)
data = DataObject.new(123456,"Important data","1/1/1","www.url.com",57)
formatted = custom_format(formatter,data)
puts formatted
#=> DATA OBJECT 123456
#=> Important data
#=> Created on 1/1/1
#=> See www.url.com for more %infoz
#=> We are 57% done
Это регулярное выражение позволяет %x
, %xyzzy
, и даже %F13
и %z_x_y
. Это позволяет пользователю использовать литерал %
в любом месте, если за ним не следует известное значение.
Обратите внимание, что если ваш объект не имеет метод доступа, вы можете вместо этого использовать:
# Replace %foo in format string with value @foo inside obj
# If the value is `nil` or `false` the original placeholder will be used
def custom_format(format, obj)
format.gsub(/%([a-z]\w*)/i){ |s| obj.instance_variable_get(:"@#{$1}") || s }
end
... но идущий непосредственно в объект для переменного экземпляра, вероятно, не самая лучшая идея.
дан общие или конкретные "строки форматирования":
gs = "Hour %d, minute %d"
fs = "Hour %H, minute %M"
... вы можете создать "отформатированную строку" по:
Использование sprintf
или String#%
с общей строкой
s = sprintf(gs, 1, 2) #=> "Hour 1, minute 2"
s = gs % [1,2] #=> "Hour 1, minute 2"
Использование Time#strftime
с временным объектом (и правильные значения заполнителей, согласно документации):
s = Time.now.strftime(fs) #=> "Hour 10, minute 08"
... вы можете 'разбор' непечатаемые строки по расщеплению на %
:
pieces = gs.split(/(%[^%\s])/) #=> ["Hour ", "%d", ", minute ", "%d"]
... в большинстве случаев вы можете, как правило, извлекать значения из формы atted строка с помощью строки форматирования с этим кодом (только слегка тестировалась):
# With s="Hour 10, minute 08"
parts = s.match /\A#{fs.gsub(/%([^%\s])/o,'(?<\1>.+?)')}\z/
p parts[:H] #=> "10"
p parts[:M] #=> "08"
# If the formatting string uses the same placeholder more than once
# you will need to ask for the parts by index, not by name
parts = s.match /\A#{gs.gsub(/%([^%\s])/o,'(?<\1>.+?)')}\z/
p parts[1] #=> "10"
p parts[2] #=> "08"
Это волшебная линия шум преобразует строку форматирования в регулярное выражение, которая захватывает и имена каждого заполнитель:
"Hour %H, minute %M"
/\AHour (?<H>.+?), minute (?<M>.+?)\z/
MatchData
возвращается, когда вы сопоставляете строку с этим регулярным выражением, отслеживая все части, как по имени, так и по индексу.
Редактировать: Вот более надежное решение для сканирования строки с форматировщиком, который обрабатывает Sprintf форматирование заполнителей, такие как %-3d
или %0.3f
:
require 'strscan'
def scan_with_format(format, str)
s = StringScanner.new(format)
parts = []
accum = ""
until s.eos?
if (a=s.scan(/%%/)) || (b=s.scan(/%[^a-z]*[a-z]/i))
parts << Regexp.escape(accum) unless accum.empty?
accum = ""
parts << (a ? '%' : "(?<#{b[-1]}>.+?)")
else
accum << s.getch
end
end
parts << Regexp.escape(accum) unless accum.empty?
re = /\A#{parts.join}\z/
str.match(re)
end
в действии:
formatter = "Hour %02d, minute %d, %.3fs $%d and %0d%% done"
formatted = formatter % [1, 2, 3.4567, 8, 90 ]
#=> "Hour 01, minute 2, 3.457s $8 and 90% done"
parts = scan_with_format(formatter, formatted)
#=> #<MatchData "Hour 01, minute 2, 3.457s $8 and 90% done" d:"01" d:"2" f:"3.457" d:"8" d:"90">
Простой 'gsub' должен отлично работать –
Не могли бы вы пояснить, что вы подразумеваете под« синтаксическими строками » как это"? Вы пытаетесь a) взять строку форматирования (с '%' в ней) и разбить ее как-нибудь? b) Возьмите строку форматирования и форматированную строку (со значениями вместо '%'), а затем извлеките значения? c) Для произвольной строки форматирования получить форматированную строку (заменить «%' placeholders на значения)? – Phrogz
@ Серхио Туленцев. Вы правы, это будет отлично работать ... Полагаю, я переусердствовал. Благодаря! – NcAdams