Форматирование именованных строк в C # - PullRequest
154 голосов
/ 01 октября 2008

Есть ли способ отформатировать строку по имени, а не по позиции в C #?

В python я могу сделать что-то вроде этого примера (бессовестно украденного у здесь ):

>>> print '%(language)s has %(#)03d quote types.' % \
      {'language': "Python", "#": 2}
Python has 002 quote types.

Есть ли способ сделать это в C #? Скажем, например:

String.Format("{some_variable}: {some_other_variable}", ...);

Было бы неплохо сделать это, используя имя переменной, но допустим и словарь.

Ответы [ 18 ]

127 голосов
/ 01 октября 2008

Нет встроенного метода для обработки этого.

Вот один метод

string myString = "{foo} is {bar} and {yadi} is {yada}".Inject(o);

Вот еще

Status.Text = "{UserName} last logged in at {LastLoginDate}".FormatWith(user);

Третий улучшенный метод, частично основанный на двух выше , от Фила Хаака

45 голосов
/ 05 января 2009

У меня есть реализация, которую я только что разместил в своем блоге здесь: http://haacked.com/archive/2009/01/04/fun-with-named-formats-string-parsing-and-edge-cases.aspx

В нем рассматриваются некоторые проблемы, возникающие у этих других реализаций при экранировании скобок. У поста есть подробности. Это тоже делает DataBinder.Eval, но все еще очень быстро.

40 голосов
/ 01 декабря 2014

Интерполированные строки были добавлены в C # 6.0 и Visual Basic 14

Оба были представлены через новый Roslyn компилятор в Visual Studio 2015 .

Интересные функции (в Visual Studio 2015 IDE):

  • поддерживается синтаксическая раскраска - переменные, содержащиеся в строках, подсвечиваются
  • поддерживается рефакторинг - при переименовании переменные, содержащиеся в строках, тоже переименовываются
  • на самом деле поддерживаются не только имена переменных, но и выражений - например, не только {index} работает, но и {(index + 1).ToString().Trim()}

Наслаждайтесь! (и нажмите «Отправить улыбку» в VS)

39 голосов
/ 02 ноября 2010

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

    public string Format(string input, object p)
    {
        foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(p))
            input = input.Replace("{" + prop.Name + "}", (prop.GetValue(p) ?? "(null)").ToString());

        return input;
    }

Конечно, для разбора форматирования потребуется больше кода, но вы можете отформатировать строку с помощью этой функции, например:

Format("test {first} and {another}", new { first = "something", another = "something else" })
14 голосов
/ 01 октября 2008

Кажется, нет способа сделать это из коробки. Тем не менее, выглядит возможным реализовать собственную IFormatProvider, которая ссылается на IDictionary для значений.

var Stuff = new Dictionary<string, object> {
   { "language", "Python" },
   { "#", 2 }
};
var Formatter = new DictionaryFormatProvider();

// Interpret {0:x} where {0}=IDictionary and "x" is hash key
Console.WriteLine string.Format(Formatter, "{0:language} has {0:#} quote types", Stuff);

Выходы:

Python has 2 quote types

Предостережение заключается в том, что вы не можете смешивать FormatProviders, поэтому необычное форматирование текста нельзя использовать одновременно.

9 голосов
/ 01 октября 2008

Сам фреймворк не предоставляет способа сделать это, но вы можете взглянуть на этот пост Скотта Хансельмана. Пример использования:

Person p = new Person();  
string foo = p.ToString("{Money:C} {LastName}, {ScottName} {BirthDate}");  
Assert.AreEqual("$3.43 Hanselman, {ScottName} 1/22/1974 12:00:00 AM", foo); 

Этот код Джеймса Ньютона-Кинга похож и работает с подсвойствами и индексами,

string foo = "Top result for {Name} was {Results[0].Name}".FormatWith(student));

Код Джеймса использует System.Web.UI.DataBinder для анализа строки и требует ссылки на System.Web, что некоторым людям не нравится делать в не-веб-приложениях.

РЕДАКТИРОВАТЬ: Да, и они прекрасно работают с анонимными типами, если у вас нет объекта с готовыми свойствами для него:

string name = ...;
DateTime date = ...;
string foo = "{Name} - {Birthday}".FormatWith(new { Name = name, Birthday = date });
6 голосов
/ 05 января 2009

См. https://stackoverflow.com/questions/271398?page=2#358259

С помощью связанного расширения вы можете написать это:

var str = "{foo} {bar} {baz}".Format(foo=>"foo", bar=>2, baz=>new object());

и вы получите "foo 2 System.Object ".

4 голосов
/ 01 октября 2008

Я думаю, что ближе всего вы получите индексированный формат:

String.Format("{0} has {1} quote types.", "C#", "1");

Существует также String.Replace (), если вы хотите сделать это в несколько шагов и поверить, что вы не найдете свои «переменные» где-либо еще в строке:

string MyString = "{language} has {n} quote types.";
MyString = MyString.Replace("{language}", "C#").Replace("{n}", "1");

Расширение для использования списка:

List<KeyValuePair<string, string>> replacements = GetFormatDictionary();  
foreach (KeyValuePair<string, string> item in replacements)
{
    MyString = MyString.Replace(item.Key, item.Value);
}

Вы также можете сделать это с помощью Dictionary , перебирая его коллекции .Keys, но используя List >, мы можем воспользоваться преимуществом метода List .ForEach () и сжать обратно на однострочник:

replacements.ForEach(delegate(KeyValuePair<string,string>) item) { MyString = MyString.Replace(item.Key, item.Value);});

Лямбда была бы еще проще, но я все еще на .Net 2.0. Также обратите внимание, что производительность .Replace () не является звездной при многократном использовании, поскольку строки в .Net являются неизменяемыми. Кроме того, для этого необходимо, чтобы переменная MyString была определена таким образом, чтобы она была доступна делегату, поэтому она еще не идеальна.

3 голосов
/ 19 апреля 2014

Моя библиотека с открытым исходным кодом, Regextra , поддерживает именованное форматирование (среди прочего). В настоящее время он нацелен на .NET 4.0+ и доступен на NuGet . У меня также есть вступительное сообщение в блоге об этом: Regextra: помогает вам уменьшить ваши (проблемы) {2} .

Именованный бит форматирования поддерживает:

  • Основное форматирование
  • Форматирование вложенных свойств
  • Словарь форматирования
  • Экранирование разделителей
  • Стандартное / Пользовательское / IFormatProvider форматирование строки
* * 1 022 Пример: * 1 023 *
var order = new
{
    Description = "Widget",
    OrderDate = DateTime.Now,
    Details = new
    {
        UnitPrice = 1500
    }
};

string template = "We just shipped your order of '{Description}', placed on {OrderDate:d}. Your {{credit}} card will be billed {Details.UnitPrice:C}.";

string result = Template.Format(template, order);
// or use the extension: template.FormatTemplate(order);

Результат:

Мы только что отправили ваш заказ «Виджет», размещенный 28.02.2014. С вашей кредитной карты будет списана сумма в размере 1500 долларов США.

Проверьте ссылку на проект GitHub (выше) и wiki для других примеров.

2 голосов
/ 22 сентября 2011

Отметьте это:

public static string StringFormat(string format, object source)
{
    var matches = Regex.Matches(format, @"\{(.+?)\}");
    List<string> keys = (from Match matche in matches select matche.Groups[1].Value).ToList();

    return keys.Aggregate(
        format,
        (current, key) =>
        {
            int colonIndex = key.IndexOf(':');
            return current.Replace(
                "{" + key + "}",
                colonIndex > 0
                    ? DataBinder.Eval(source, key.Substring(0, colonIndex), "{0:" + key.Substring(colonIndex + 1) + "}")
                    : DataBinder.Eval(source, key).ToString());
        });
}

Пример:

string format = "{foo} is a {bar} is a {baz} is a {qux:#.#} is a really big {fizzle}";
var o = new { foo = 123, bar = true, baz = "this is a test", qux = 123.45, fizzle = DateTime.Now };
Console.WriteLine(StringFormat(format, o));

Производительность довольно хорошая по сравнению с другими решениями.

...