2

このようにフォーマットされた文字列を使用するツールがいくつかあることに気付きました (単純な任意の例を示すため):

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

ここで、%(文字) は変数を表します。たとえば、PHP の「strptime/strftime」コマンドは、%(文字) を使用して日付/時刻のコンポーネントを表します。Git コミット ログ プリンターは、オプションの形式引数を受け取ります。ここで、%(文字) はコミットのコンポーネント (日付、作成者、説明など) を指します。

このロジックを自分の Ruby プロジェクトに適用したいと考えています。このような文字列を解析できる Ruby ライブラリまたは gem はありますか?

編集: 回答してくれたすべての人に感謝しますが、私が探しているのは Date.strftime/strptime のような特殊なタスクではありません。私のアプリは Web サイトから一連のデータをダウンロードし、それをドキュメントにコンパイルします。そのドキュメントのフォーマット方法をユーザーが選択できるようにしたいと考えています。だから私が持っているなら

#<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:最初のコメントは正しかった...単純な置換でうまくいく.

4

4 に答える 4

3

あなたが持っている正確な文字列では機能しませんが、わずかな変更で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"
于 2013-07-16T15:47:25.030 に答える
2

編集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"

…次の方法で「フォーマットされた文字列」を作成できます。

  • sprintforString#%を一般的な文字列で使用する

    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"]

…ほとんどの状況では、通常、次のコードで書式設定文字を使用して、書式設定された文字列から値を抽出できます (軽くテストしただけです)。

# 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正規表現に対して文字列を照合すると返される は、名前とインデックスの両方ですべての部分を追跡します。


編集%-3d:またはなどの sprintf 書式設定プレースホルダーを処理するフォーマッターで文字列をスキャンするためのより堅牢なソリューションを次に示します%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">
于 2013-07-16T16:19:44.613 に答える
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"
于 2013-07-16T15:44:44.933 に答える
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"
于 2013-07-16T15:50:47.593 に答える