Как предоставить пользовательский заполнитель строки для формата строки - PullRequest
26 голосов
/ 09 июля 2009

У меня есть строка

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

Я использую string.format для его форматирования.

String.Format(str, "Hello");

Теперь, если я хочу, чтобы пациент также был извлечен из некоторого конфига, мне нужно изменить str на что-то вроде "Enter {0} {1} name". Таким образом, он заменит {1} вторым значением. Проблема в том, что я хочу вместо {1} какого-то другого формата что-то вроде {pat}. Но когда я пытаюсь использовать, выдает ошибку. Причина, по которой я хочу использовать другой формат, заключается в том, что существует множество файлов, которые мне нужно изменить следующим образом (которые могут содержать {0}, {1} и т. Д.). Поэтому мне нужен собственный заполнитель, который можно заменить во время выполнения.

Ответы [ 8 ]

51 голосов
/ 09 июля 2009

Возможно, вы захотите проверить FormatWith 2.0 от Джеймс Ньютон-Кинг . Это позволяет использовать имена свойств в качестве токенов форматирования, например:

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

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

Вы также можете использовать его с анонимными типами.

ОБНОВЛЕНИЕ: Существует также аналогичное решение от Скотт Хансельман , но оно реализовано как набор методов расширения на Object вместо String.

ОБНОВЛЕНИЕ 2012 : вы можете получить Calrius Consulting NETFx String.Format с методом расширения NuGet пакет на NuGet.org

ОБНОВЛЕНИЕ 2014 : Существует также StringFormat.NET и StringFormat littlebit

17 голосов
/ 09 июля 2009

Regex с MatchEvaluator кажется хорошим вариантом:

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."
}
3 голосов
/ 23 февраля 2012
object[] myInts = new int[] {8,9}; 

Однако вы можете обойтись без:

object[] myInts = new string[] { "8", "9" }; 
string bar = string.Format("{0} {1}", myInts); 
3 голосов
/ 09 июля 2009

Я видел все ответы выше, но не смог правильно ответить на вопрос :)

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

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

string result = "Enter " + myFirstStr + " " + mySecondStr + " name";
2 голосов
/ 19 февраля 2013

Вот еще одна версия, которую я нашел здесь: 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();
} 
1 голос
/ 19 августа 2014

Вы также можете использовать пример из 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));
0 голосов
/ 30 августа 2017

Я хотел что-то более похожее на форматирование строк в 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 голосов
/ 09 июля 2009

Возможно, вам лучше использовать Replace для настраиваемого поля и Format для остальных, например:

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