6

YAMLを介してPerlとRubyの間でデータを交換するのに問題があります。のように、number:numberのような値がいくつかあります1:16

PerlのYAMLライブラリ(TinyおよびXS)は、これ1:16を引用符なしでエンコードします。RubyのYAMLライブラリ(Psych)はこれを文字列として解釈しませんが、代わりにどういうわけかFixnum値になります4560。どちらの側でもこの変換の問題を修正する方法がわかりません。

私のユースケースのYAMLのすべての値は、オブジェクトまたは文字列である必要があります。したがって、そのようなオプションが存在する場合は、PerlYAMLライブラリにすべての値を引用するように指示できます。または、すべての値を文字列として解釈するようにRuby YAMLライブラリに指示する方法はありますか?何か案は?

どちらかの側で言語を変更することは、ロジスティック的にオプションではありません。

Perl:

use YAML::XS qw(DumpFile);
my $foo={'abc'=>'1:16'};
DumpFile('test.yaml',$foo);

ルビー:

require('yaml')
foo=YAML.load_file('test.yaml')
puts(foo['abc'])

Rubyコードはを出力します45604560コメントの1つは、どのように取得するかを理解しました1:16。これは1時間、16分を秒に変換したものです。ええと、わかりました。

4

3 に答える 3

6

Yaml 1.1 仕様によると、1:1660 進数 (base 60) 形式の整数です。

http://yaml.org/type/int.htmlも参照してください。

「:」を使用すると、基数 60 で整数を表現できます。これは、時間と角度の値に便利です。

Ruby に含まれる Yaml パーサーである Psych は、こ​​の形式を認識し、値を整数に変換します(間違って、1:1671 にする必要があります。Psych コードは、そのような値がすべてこの形式になると想定しているようですa:b:cが、正規表現はそれを強制しません)。 . Perl エミッター (少なくとも私がテストした YAML::XS) はこの形式を認識しないため、ファイルを書き込むときに文字列を引用しません。YAML::XSいくつかの整数を認識して引用しますが、すべてではありません。YAML::XS は、Psych が認識できる他の多くの形式 (日付など) も認識しません。

(60 進数形式は Yaml 1.2 仕様から削除されたようです。)

Psych は、その解析においてかなりの柔軟性を可能にしますYAML.load_file。これは、一般的な使用例のためのシンプルなインターフェースです。

Psychのメソッドを使用しparseて yaml のツリー表現を作成し、カスタムScalarScanner(特定の形式の文字列を適切な Ruby 型に変換するオブジェクト) を使用してこれを Ruby データ構造に変換できます。

require('yaml')

class MyScalarScanner < Psych::ScalarScanner
  def tokenize string
    #this is the same regexp as Psych uses to detect base 60 ints:
    return string if string =~ /^[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+$/
    super
  end
end

tree = YAML::parse_file 'test.yaml'
foo = Psych::Visitors::ToRuby.new(MyScalarScanner.new).accept tree

YAML.load_fileこれは、カスタマイズされたスキャナ クラスを使用する点を除いて、基本的に を使用する場合と同じプロセスです。

同様の代替手段は、メソッドを開いてカスタマイズしScalarScannerたメソッドに置き換えることです。tokenizeこれにより、よりシンプルなインターフェースを使用できるようになりますが、load_fileモンキー パッチ クラスに関する通常の注意事項があります。

class Psych::ScalarScanner
  alias :orig_tokenize :tokenize
  def tokenize string
    return string if string =~ /^[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+$/
    orig_tokenize string
  end
end

foo = YAML.load_file 'test.yaml'

これらの例では、 のような形式の値のみが考慮されることに注意してください1:16。Perl プログラムが発行するものによっては、他のパターンもオーバーライドする必要がある場合があります。特に注目したいのは、60 進浮動小数点数 (例: 1:16.44) です。

于 2012-09-27T16:38:56.783 に答える
1

使用しているパーサーにバグがあります。1:16ある種の時間だと考えているようですが (4560 は 1 時間 16 分の秒数であるため)、その解釈を検証するものは何も見つかりません。

最良の解決策は、バグのないパーサーを使用することです。

  • libyaml、YAML::XS によって使用され、おそらく Ruby バインディングを持っています。
  • libsyck、YAML::Syck によって使用され、おそらく Ruby バインディングを持っています。

別の方法は、文字列が常に引用符で囲まれている (または少なくとも時間として扱われる場合) YAML を生成することです。

YAML::Syckには、まさにそれを行うためのオプションがあります。

$ perl -e'
   use YAML::Syck qw( Dump );
   local $YAML::Syck::SingleQuote = 1;
   print(Dump({abc=>"1:16"}));
'
--- 
"abc": '1:16'

(以前にこのオプションを見逃した理由がわかりません!)

于 2012-09-27T00:00:49.003 に答える
-4

Ruby は、いくつかの特別な形式に適合しない限り、すべての YAML エントリを文字列として解釈します。エントリ1:16は一時的に特殊な形式に一致するように見えるため、Ruby はそれを誤って解釈しています。

Ruby にフィールドを文字列として解釈させる必要があります。これには 2 つの方法があります。次の YAML 出力のいずれかで、必要な結果が得られるはずです。

abc: !str 1:16
abc: '1:16'

この出力を生成するには、次の Perl コードを試してください。

my $foo={'abc'=>'!str 1:16'};
my $foo={'abc'=>"'1:16'"};

更新: 次のコードを使用して、Perl と Ruby の間でデータを渡すことができました。

パール:

use YAML::XS qw(DumpFile);
my $foo={'abc'=>'1:16'};
DumpFile('test.yaml',$foo);

ルビー:

require 'yaml'
foo=YAML.parse_file('test.yaml')
foo['abc'].value
=> "1:16"
foo['abc'].value.class
=> String

結果は、返される単純なハッシュよりも使用するのが少し複雑ですがload_file、少なくとも期待どおりにファイルを解析しているように見えます。

于 2012-09-26T21:26:44.383 に答える