2013-07-16 2 views
2

Я заметил, что есть несколько инструментов, которые используют строки, отформатированные как это (дать простой произвольный пример):Как разобрать строки печати формата стиль в Рубине

"Hour %h, minute %m, a total of %S seconds have passed" 

где% (письмо) представляет переменную. Например, команда «strptime/strftime» PHP использует% (letter) для представления компонента даты/времени. Принтер журнала фиксации Git принимает необязательный аргумент формата, где% (буква) относится к компоненту фиксации (дата, автор, описание и т. Д.).

Я ищу эту логику для своего собственного проекта Ruby. Есть ли библиотека Ruby или драгоценный камень, который может анализировать такие строки?

EDIT: Большое спасибо всем, кто ответил, но то, что я ищу, не является специализированной задачей, как Date.strftime/strptime. Мое приложение загружает набор данных с веб-сайта, а затем компилирует его в документ, и я хочу, чтобы пользователи могли выбирать способ форматирования этого документа. Так что, если у меня есть

#<DataObject:0x007fec299de348 
@id=123456, 
@name="Important data", 
@date="1/1/1", 
@url="www.url.com"> 

и пользователь вводит строку форматирования, как это:

DATA OBJECT %i 
%n 
Created on %d 
See %u for more 

Результат должен выглядеть следующим образом:

DATA OBJECT 123456 
Important data 
Created on 1/1/1 
See www.url.com for more 

Есть библиотека, которая делает что-то общее или я должен сам программировать эту логику?

РЕДАКТИРОВАТЬ # 2: Первый комментарий получил это правильно ... простая замена будет работать нормально.

+2

Простой 'gsub' должен отлично работать –

+0

Не могли бы вы пояснить, что вы подразумеваете под« синтаксическими строками » как это"? Вы пытаетесь a) взять строку форматирования (с '%' в ней) и разбить ее как-нибудь? b) Возьмите строку форматирования и форматированную строку (со значениями вместо '%'), а затем извлеките значения? c) Для произвольной строки форматирования получить форматированную строку (заменить «%' placeholders на значения)? – Phrogz

+0

@ Серхио Туленцев. Вы правы, это будет отлично работать ... Полагаю, я переусердствовал. Благодаря! – NcAdams

ответ

1

Вы можете использовать DateTime.strptime/DateTime#strftime:

>> require 'date' 
=> false 
>> DateTime.strptime("Hour 14, minute 28, a total of 54 seconds have passed", "Hour %H, minute %M, a total of %S seconds have passed") 
=> #<DateTime: 2013-07-17T14:28:54+00:00 ((2456491j,52134s,0n),+0s,2299161j)> 
>> _.strftime('%H:%M:%S') 
=> "14:28:54" 
2

Он не работает с точной строкой у вас есть, но с небольшим изменением, вы можете использовать метод String#%.

s = "Hour %{h}, minute %{m}, a total of %{S} seconds have passed" 
s % {h: 3, m: 57, S: 24} 
# => "Hour 3, minute 57, a total of 24 seconds have passed" 
+0

В случае, если я прав, вы не можете изменить его, если он не находится в течение пяти минут, или кто-то редактирует мой ответ. (Но это не большая проблема) – sawa

+0

Похоже, вы были правы. +1 :) – Phrogz

1

Для замены нормальных строк есть printf/sprintf:

> printf('Hello %s', 'World') 
Hello World 

Для даты строки смотрите на Time#strftime

> Time.now.strftime('%Y-%m-%d %H:%M:%S') 
=> "2013-07-16 23:49:52" 
2

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"> 
+0

Я обновил свой ответ простым и быстрым способом использования 'gsub' для динамического поиска значений в объекте за один проход. – Phrogz