2009-09-11 17 views
14

一般的なフォーマッタ/パーサーの組み合わせを作成しようとしています。フォーマットされた文字列の解析

例のシナリオ:

  • IはString.Formatの(文字列を有する)、例えばvar format = "{0}-{1}"
  • 私は入力用のオブジェクト(文字列)の配列を持っています。 var arr = new[] { "asdf", "qwer" }
  • 私はフォーマット文字列を使用して配列をフォーマットしています。 var res = string.Format(format, arr)

私がしようとしているのは、フォーマットされた文字列をオブジェクトの配列(文字列)に戻すことです。何かのような(擬似コード):

var arr2 = string.Unformat(format, res) 

// when: res = "asdf-qwer"  
// arr2 should be equal to arr 

誰でもこのようなことを経験していますか?私は正規表現の使用について考えています(元のフォーマット文字列を変更し、それをRegex.Matchesに渡して配列を取得します)、フォーマット文字列の各プレースホルダに対して実行します。これは実現可能なのでしょうか、あるいは他のより効率的なソリューションがありますか?

+0

フォーマットされていない文字列はどのくらいの期間ですか? –

+0

@Chris:妥当な範囲内です。 ATM、私はファイル名にしか使用していません。 –

+0

一般に、結果はあいまいかもしれないことに注意してください。 'format = '{0} - {1}''と 'arr = {" as-df "、" qw-er "}'です。 3つの異なる方法でフォーマット解除することができます。あいまいさに対処する方法を定義するか、書式文字列の内容と値を制限する必要があります。 – peterchen

答えて

9

情報が失われたためにフォーマットを解除することはできません。 String.Formatは "破壊的な"アルゴリズムであり、あなたは(いつでも)元に戻ることはできません。

あなたは、"{0}-{1}"を追跡部材及び{ "asdf", "qwer" }を追加ToString()をオーバーライドして、あなたのコードを少し変更しstringから継承する新しいクラスを作成します。

あまりにも扱いにくい場合は、同じクラスを作成しますが、stringから継承せず、コードを少し変更してください。

IMO、これはこれを実行する最善の方法です。

+0

もう少し仕事はありますが、実現可能です。 –

2

一般的なケースでは単純にできません。一部の情報は、Formatメソッドでは "失われた"(文字列の境界)です。想定:

String.Format("{0}-{1}", "hello-world", "stack-overflow"); 

どのように "アンフォーマット"しますか?

+0

良い点。どのような形式の文字がないという仮定がオブジェクトの配列に存在する一般的なソリューションよりも少ないものを作成するのはどうでしょうか? –

+4

Adrian: 'String.Format(" {0} {1} "、" 12 "、" 3 ")'は123を返しますが、フォーマット文字列から推測することはできません"12"、 "3"、 "12"、 "3"、または... –

+0

結果の配列を返し、クライアントに処理させます。 – toddmo

2

「 - 」が元の文字列に含まれていないと仮定すると、分割のみを使用できますか?

var arr2 = formattedString.Split('-'); 

これは、前提を前提として提示された例にのみ適用されます。リバースアルゴリズムは、使用される書式の種類に依存します。逆の操作は、他の回答に記されているように、不可能でさえあるかもしれない。

+0

形式は何でもかまいません。しかし、はい、私たちは、書式化された配列に何も書かれてはならないということに同意する必要があります。 –

+0

答えにいくつかの明確な点を追加しました。 –

1

簡単な解決策は、正規表現マッチが非貪欲作る

  • エスケープformat
  • 内の他のすべての特別charatersを(。*)で、すべての書式トークンを置き換える

    • にあるかもしれない

    これは、可能な限り最短のマッチに曖昧さを解決します。

    (私は、そう私を修正してください、人々を正規表現が得意ではないよ:))フォーマット後

  • 0

    、あなたがキーとして文字列を辞書に結果の文字列やオブジェクトの配列を置くことができます。

    Dictionary<string,string []> unFormatLookup = new Dictionary<string,string []> 
    ... 
    var arr = new string [] {"asdf", "qwer" }; 
    var res = string.Format(format, arr); 
    unFormatLookup.Add(res,arr); 
    

    とアンフォーマット方式では、あなたは単純に文字列を渡すと、その文字列を検索し、使用される配列を返すことができます。

    string [] Unformat(string res) 
    { 
        string [] arr; 
        unFormatLoopup.TryGetValue(res,out arr); //you can also check the return value of TryGetValue and throw an exception if the input string is not in. 
        return arr; 
    } 
    
    14

    失われた情報についてのコメントが有効ですが、いつかあなたはただ知られている書式の文字列の文字列値を取得したいだけです。

    1つの方法は、私の友人によって書かれたthis blog postです。彼はstring[] ParseExact()という拡張メソッドを実装しました。これはDateTime.ParseExact()に似ています。データは文字列の配列として返されますが、それを使って生きることができれば、大変便利です。

    public static class StringExtensions 
    { 
        public static string[] ParseExact(
         this string data, 
         string format) 
        { 
         return ParseExact(data, format, false); 
        } 
    
        public static string[] ParseExact(
         this string data, 
         string format, 
         bool ignoreCase) 
        { 
         string[] values; 
    
         if (TryParseExact(data, format, out values, ignoreCase)) 
          return values; 
         else 
          throw new ArgumentException("Format not compatible with value."); 
        } 
    
        public static bool TryExtract(
         this string data, 
         string format, 
         out string[] values) 
        { 
         return TryParseExact(data, format, out values, false); 
        } 
    
        public static bool TryParseExact(
         this string data, 
         string format, 
         out string[] values, 
         bool ignoreCase) 
        { 
         int tokenCount = 0; 
         format = Regex.Escape(format).Replace("\\{", "{"); 
    
         for (tokenCount = 0; ; tokenCount++) 
         { 
          string token = string.Format("{{{0}}}", tokenCount); 
          if (!format.Contains(token)) break; 
          format = format.Replace(token, 
           string.Format("(?'group{0}'.*)", tokenCount)); 
         } 
    
         RegexOptions options = 
          ignoreCase ? RegexOptions.IgnoreCase : RegexOptions.None; 
    
         Match match = new Regex(format, options).Match(data); 
    
         if (tokenCount != (match.Groups.Count - 1)) 
         { 
          values = new string[] { }; 
          return false; 
         } 
         else 
         { 
          values = new string[tokenCount]; 
          for (int index = 0; index < tokenCount; index++) 
           values[index] = 
            match.Groups[string.Format("group{0}", index)].Value; 
          return true; 
         } 
        } 
    } 
    
    +0

    この状況で返されるもの: '' a-b-c '' .ParseExact( "{0} - {1} - {0}") '? – Zarepheth

    +0

    提案 - 'と' 'format =置き換え(トークン、string.Format("(? 'グループ{0}'。*) "、tokenCount))を置き換えてください。format = format.ReplaceFirst(token、string.Format ( "(? 'group {0}'。*)"、tokenCount)); format = format.Replace(token、string.Format( "\\ {0}"、tokenCount)); '。これは、入力パラメータを複数回使用する書式文字列をよりよく処理する必要があります。 ReplaceFirstは次の場所から来ました:http://stackoverflow.com/questions/141045/how-do-i-replace-the-first-instance-of-a-string-in-net#141076 – Zarepheth

    +0

    "abc" .ParseExact( "" {0} $ - \\&* {1}^{1} {2} 2}」) – CRice

    関連する問題