Подтвердить что ты не робот

Как предоставить пользовательский строковый заполнитель для строкового формата

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

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

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

String.Format(str, "Hello");

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

4b9b3361

Ответ 1

Возможно, вы захотите проверить 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));

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

UPDATE: Существует также решение Scott Hanselman, но он реализован как набор методов расширения на Object вместо String.

ОБНОВЛЕНИЕ 2012. Вы можете получить Calrius Consulting NETFx String.FormatWith Extension Method Пакет NuGet на NuGet.org

UPDATE 2014. Существует также StringFormat.NET и littlebit StringFormat

Ответ 2

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

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

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

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

string result = "Enter " + myFirstStr + " " + mySecondStr + " name";

Ответ 4

object[] myInts = new int[] {8,9}; 

Однако вы можете сойти с рук:

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

Ответ 5

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

Ответ 6

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

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

Ответ 7

Вы также можете использовать пример из класса Marc Gravell и Extend the 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));

Ответ 8

Я хотел что-то, что больше походило на форматирование строки 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