web-dev-qa-db-ja.com

文字列形式のカスタム文字列プレースホルダーを提供する方法

紐があります

string str ="Enter {0} patient name";

私はそれをフォーマットするためにstring.formatを使用しています。

String.Format(str, "Hello");

ここで、患者もいくつかの構成から取得したい場合は、strを"Enter {0} {1} name"のようなものに変更する必要があります。したがって、{1}を2番目の値に置き換えます。問題は、{1}の代わりに{pat}のような他の形式が欲しいということです。しかし、使用しようとすると、エラーがスローされます。別のフォーマットが必要な理由は、このように変更する必要のあるファイルがたくさんあるためです({0}、{1}などが含まれる場合があります)。したがって、実行時に置き換えることができるカスタムプレースホルダーが必要です。

26
Punit Singhi

FormatWith 2.0 by James Newton-King を確認してください。これにより、プロパティ名を次のようなフォーマットトークンとして使用できます。

var user = new User()
{
    Name = "Olle Wobbla",
    Age = 25
};

Console.WriteLine("Your name is {Name} and your age is {Age}".FormatWith(user));

匿名型でも使用できます。

UPDATE:同様の solution by もあります。 Scott Hanselman ですが、ObjectではなくStringの拡張メソッドのセットとして実装されています。

UPDATE 2012:Calrius Consultingの NETFx String.FormatWith拡張メソッド NuGetパッケージを/で取得できます NuGet.org

UPDATE 2014StringFormat.NET および もあります/ littlebitのStringFormat

51
Magnus Lindhe

RegexMatchEvaluatorを併用するのは、良いオプションのようです。

static readonly Regex re = new Regex(@"\{([^\}]+)\}", RegexOptions.Compiled);
static void Main()
{
    string input = "this {foo} is now {bar}.";
    StringDictionary fields = new StringDictionary();
    fields.Add("foo", "code");
    fields.Add("bar", "working");

    string output = re.Replace(input, delegate (Match match) {
        return fields[match.Groups[1].Value];
    });
    Console.WriteLine(output); // "this code is now working."
}
17
Marc Gravell
object[] myInts = new int[] {8,9}; 

ただし、次の方法で回避できます。

object[] myInts = new string[] { "8", "9" }; 
string bar = string.Format("{0} {1}", myInts); 
3
mujtaba Hyder

上記のすべての回答を見ましたが、質問を正しく答えることができませんでした:)

次のコードが要件を満たさない特別な理由はありますか?

string myFirstStr = GetMyFirstStrFromSomewhere();
string mySecondStr = GetMySecondStrFromSomewhere();

string result = "Enter " + myFirstStr + " " + mySecondStr + " name";
3
Chansik Im

これが私がここで見つけた別のバージョンです: http://www.reddit.com/r/programming/comments/bodml/beef_up_params_in_c_5_to_solve_lambda_abuse/c0nrsf1

これに対するすべての解決策はリフレクションを伴いますが、これは理想的ではありませんが、他のいくつかの主要なパフォーマンス問題が解決された彼のコードです。 (エラーチェックはありません。必要に応じて追加してください。):

1)直接ランタイムリフレクションを使用し、DataBinderオーバーヘッドはありません

2)正規表現を使用せず、シングルパスの解析と状態を使用します。

3)文字列を中間文字列に変換してから、再度最終的な形式に変換しません。

4)すべての場所で文字列を更新して新しい文字列に連結するのではなく、単一のStringBuilderで割り当ておよび連結します。

5)n個の置換操作でデリゲートを呼び出すことによるスタックオーバーヘッドを削除します。

6)一般に、比較的直線的な方法でスケーリングする単一のパススルーです(まだ各プロップ検索とネストされたプロップ検索にいくらかコストがかかりますが、それだけです)。

public static string FormatWith(this string format, object source)
{
    StringBuilder sbResult = new StringBuilder(format.Length);
    StringBuilder sbCurrentTerm = new StringBuilder();
    char[] formatChars = format.ToCharArray();
    bool inTerm = false;
    object currentPropValue = source;

    for (int i = 0; i < format.Length; i++)
    {
        if (formatChars[i] == '{')
            inTerm = true;
        else if (formatChars[i] == '}')
        {
            PropertyInfo pi = currentPropValue.GetType().GetProperty(sbCurrentTerm.ToString());
            sbResult.Append((string)(pi.PropertyType.GetMethod("ToString", new Type[]{}).Invoke(pi.GetValue(currentPropValue, null), null)));
            sbCurrentTerm.Clear();
            inTerm = false;
            currentPropValue = source;
        }
        else if (inTerm)
        {
            if (formatChars[i] == '.')
            {
                PropertyInfo pi = currentPropValue.GetType().GetProperty(sbCurrentTerm.ToString());
                currentPropValue = pi.GetValue(source, null);
                sbCurrentTerm.Clear();
            }
            else
                sbCurrentTerm.Append(formatChars[i]);
        }
        else
            sbResult.Append(formatChars[i]);
    }
    return sbResult.ToString();
} 
2
Bryan Legend

Marc Gravellの例を使用して、Stringクラスオブジェクトを拡張することもできます。

public static class StringExtension
{
    static readonly Regex re = new Regex(@"\{([^\}]+)\}", RegexOptions.Compiled);
    public static string FormatPlaceholder(this string str, Dictionary<string, string> fields)
    {
        if (fields == null)
            return str;

        return re.Replace(str, delegate(Match match)
        {
            return fields[match.Groups[1].Value];
        });

    }
}

使用例:

String str = "I bought a {color} car";
Dictionary<string, string> fields = new Dictionary<string, string>();
fields.Add("color", "blue");

str.FormatPlaceholder(fields));
1
Ole K

次のように、カスタムフィールドにReplaceを使用し、残りにFormatを使用することをお勧めします。

string str = "Enter {0} {pat} name";
String.Format(str.Replace("{pat}", "Patient"), "Hello");
0
Dean Povey

Pythonの文字列フォーマットのように機能するものが欲しかったので、これを書きました: https://Gist.github.com/samwyse/b225b32ae1aea6fb27ad9c966b9ca90b

次のように使用します。

Dim template = New FormatFromDictionary("{cat} vs {dog}")
Dim d = New Dictionary(Of String, Object) From {
    {"cat", "Felix"}, {"dog", "Rex"}}
Console.WriteLine(template.Replace(d)) ' Felix vs Rex
0
samwyse