Составное форматирование

В качестве входных данных для составного форматирования в .NET используется список объектов и строка составного формата. Строка составного формата состоит из фиксированного текста, пересекаемого с индексными заполнителями, называемыми элементами форматирования. Эти элементы формата соответствуют объектам в списке. Операция форматирования создает результирующую строку, состоящую из исходного фиксированного текста, в который включено строковое представление объектов из списка.

Важно!

Вместо использования строк составного формата можно использовать интерполированные строки , если язык и его версия поддерживают их. Интерполированная строка содержит интерполированные выражения. Каждое интерполированное выражение завершается значением выражения и включается в строку результатов, если строка назначена. Дополнительные сведения см. в разделе Интерполяция строк (справочник по C#) и Интерполированные строки (справочник по Visual Basic).

Компонент составного форматирования поддерживается следующими методами:

  • Метод String.Format, который возвращает отформатированную результирующую строку.
  • Метод StringBuilder.AppendFormat, который добавляет отформатированную результирующую строку в объект StringBuilder.
  • Некоторые перегруженные версии метода Console.WriteLine, которые отображают отформатированную результирующую строку в консоли.
  • Некоторые перегруженные версии метода TextWriter.WriteLine, которые записывают отформатированную результирующую строку в поток или файл. Классы, производные от TextWriter, например StreamWriter и HtmlTextWriter, также поддерживают эту функцию.
  • Метод Debug.WriteLine(String, Object[]), который выводит отформатированное сообщение в прослушиватели трассировки.
  • Методы Trace.TraceError(String, Object[]), Trace.TraceInformation(String, Object[]) и Trace.TraceWarning(String, Object[]), которые выводят отформатированные сообщения в прослушиватели трассировки.
  • Метод TraceSource.TraceInformation(String, Object[]), который записывает информационный метод в прослушиватели трассировки.

Строка составного формата

Строка составного формата и список объектов используются в качестве аргументов методов, поддерживающих составное форматирование. Строка составного формата состоит из блоков фиксированного текста числом от нуля и больше, перемежаемых одним или несколькими элементами форматирования. Фиксированным текстом может являться произвольная строка, а каждый элемент форматирования должен соответствовать объекту или упакованной структуре из списка. В ходе составного форматирования создается новая результирующая строка, в которой все элементы форматирования заменены на строковое представление соответствующих объектов из списка.

Рассмотрим следующий Format фрагмент кода:

string name = "Fred";
String.Format("Name = {0}, hours = {1:hh}", name, DateTime.Now);
Dim name As String = "Fred"
String.Format("Name = {0}, hours = {1:hh}", name, DateTime.Now)

Фиксированным текстом здесь является Name = и , hours = . Элементы формата {0}, индекс которых 0 соответствует объекту name, и {1:hh}индекс 1 которого соответствует объекту DateTime.Now.

Форматирование синтаксиса элементов

Каждый элемент форматирования имеет следующий вид и состоит из следующих компонентов:

{index[,alignment][:formatString]}

Необходимы соответствующие фигурные скобки ({ и }) .

Компонент индекса

Обязательный компонент index, также называемый описателем параметра, — это число, определяющее соответствующий объект из списка; индексация элементов ведется от нуля. То есть элемент формата, описатель параметров которого равен 0, форматирует первый объект в списке. Элемент формата, описатель параметров которого равен 1, форматирует второй объект в списке и т. д. В следующем примере содержатся четыре описателя параметров, нумерованные от нуля до трех, для представления простых чисел меньше 10:

string primes;
primes = String.Format("Prime numbers less than 10: {0}, {1}, {2}, {3}",
                       2, 3, 5, 7 );
Console.WriteLine(primes);
// The example displays the following output:
//      Prime numbers less than 10: 2, 3, 5, 7
Dim primes As String
primes = String.Format("Prime numbers less than 10: {0}, {1}, {2}, {3}",
                       2, 3, 5, 7)
Console.WriteLine(primes)
' The example displays the following output:
'      Prime numbers less than 10: 2, 3, 5, 7

На один и тот же элемент в списке объектов может ссылаться сразу несколько элементов форматирования — достигается это путем задания одинакового описателя параметра. Например, можно отформатировать одно и то же числовое значение в шестнадцатеричном, научном и числовом формате, указав строку составного формата, например "0x{0:X} {0:E} {0:N}", как показано в следующем примере:

string multiple = String.Format("0x{0:X} {0:E} {0:N}",
                                Int64.MaxValue);
Console.WriteLine(multiple);
// The example displays the following output:
//      0x7FFFFFFFFFFFFFFF 9.223372E+018 9,223,372,036,854,775,807.00
Dim multiple As String = String.Format("0x{0:X} {0:E} {0:N}",
                                       Int64.MaxValue)
Console.WriteLine(multiple)
' The example displays the following output:
'      0x7FFFFFFFFFFFFFFF 9.223372E+018 9,223,372,036,854,775,807.00

Любой элемент форматирования может ссылаться на произвольный объект списка. Например, если имеется три объекта, можно отформатировать второй, первый и третий объект, указав строку составного формата, {1} {0} {2}например. Объект, на который не ссылается элемент формата, игнорируется. Если описатель параметра ссылается на элемент за пределами списка объектов, то во время выполнения создается исключение FormatException.

Компонент выравнивания

Необязательный компонент alignment — это целое число со знаком, которое служит для указания желательной ширины поля форматирования. Если значение выравнивания меньше длины форматируемой строки, выравнивание игнорируется, а длина форматируемой строки используется в качестве ширины поля. Форматируемые данные выравниваются в поле по правому краю, если alignment имеет положительное значение, или по левому краю, если alignment имеет отрицательное значение. При необходимости отформатированная строка дополняется пробелами. Если задано выравнивание , требуется запятая.

В следующем примере определяются два массива: один из них содержит имена сотрудников, а другой — часы, которые они работали в течение двух недель. Строка составного формата по левому краю выравнивает имена в 20-символьном поле и выравнивает их часы в 5-символьном поле. Строка стандартного формата "N1" форматирует часы с одной дробной цифрой.

using System;

public class Example
{
   public static void Main()
   {
      string[] names = { "Adam", "Bridgette", "Carla", "Daniel",
                         "Ebenezer", "Francine", "George" };
      decimal[] hours = { 40, 6.667m, 40.39m, 82, 40.333m, 80,
                                 16.75m };

      Console.WriteLine("{0,-20} {1,5}\n", "Name", "Hours");
      for (int ctr = 0; ctr < names.Length; ctr++)
         Console.WriteLine("{0,-20} {1,5:N1}", names[ctr], hours[ctr]);
   }
}
// The example displays the following output:
//       Name                 Hours
//
//       Adam                  40.0
//       Bridgette              6.7
//       Carla                 40.4
//       Daniel                82.0
//       Ebenezer              40.3
//       Francine              80.0
//       George                16.8
Module Example
    Public Sub Main()
        Dim names() As String = {"Adam", "Bridgette", "Carla", "Daniel",
                                  "Ebenezer", "Francine", "George"}
        Dim hours() As Decimal = {40, 6.667d, 40.39d, 82, 40.333d, 80,
                                   16.75d}

        Console.WriteLine("{0,-20} {1,5}", "Name", "Hours")
        Console.WriteLine()
        For ctr As Integer = 0 To names.Length - 1
            Console.WriteLine("{0,-20} {1,5:N1}", names(ctr), hours(ctr))
        Next
    End Sub
End Module
' The example displays the following output:
'       Name                 Hours
'
'       Adam                  40.0
'       Bridgette              6.7
'       Carla                 40.4
'       Daniel                82.0
'       Ebenezer              40.3
'       Francine              80.0
'       George                16.8

Форматировать строковый компонент

Необязательный компонент formatString — это строка формата, соответствующая типу отформатированного объекта. Можно указать:

  • Строка стандартного или настраиваемого числового формата, если соответствующий объект является числовым значением.
  • Строка стандартного или настраиваемого формата даты и времени, если соответствующий DateTime объект является объектом.
  • Строка формата перечисления, если соответствующий объект является значением перечисления.

Если formatString не указан, используется описатель общего формата ("G") для числового, даты и времени или типа перечисления. При использовании компонента formatString необходимо двоеточие.

В следующей таблице перечислены типы или категории типов в библиотеке классов .NET, которые поддерживают предопределенный набор строк формата, а также ссылки на статьи, в которые перечислены поддерживаемые строки формата. Форматирование строк — это расширяемый механизм, который позволяет определять новые строки формата для всех существующих типов и определять набор строк формата, поддерживаемых определяемым приложением типом.

Дополнительные сведения см. в IFormattable статьях и ICustomFormatter статьях об интерфейсе.

Тип или категория типов См.
Типы даты и времени (DateTime, DateTimeOffset) Строки стандартных форматов даты и времени

Строки настраиваемых форматов даты и времени
Типы перечисления (все типы, производные от System.Enum) Строки форматов перечисления
Числовые типы (BigInteger, Byte, Decimal, Double, Int16, Int32, Int64, SByte, Single, UInt16, UInt32, UInt64) Строки стандартных числовых форматов

Строки настраиваемых числовых форматов
Guid Guid.ToString(String)
TimeSpan Строки стандартного формата TimeSpan

Строки настраиваемого формата TimeSpan

Экранирование фигурных скобок

Начало и конец элемента форматирования обозначаются соответственно открывающей и закрывающей фигурной скобкой. Чтобы отобразить литеральную фигурную скобку или закрывающую фигурную скобку, необходимо использовать escape-последовательность. Укажите две открывающие фигурные скобки ({{) в фиксированном тексте, чтобы отобразить одну открывающую скобку ({) или две закрывающие скобки (}}) для отображения одной закрывающей скобки (}). Фигурные скобки в элементе формата интерпретируются последовательно в порядке их обнаружения. Интерпретация вложенных фигурных скобок не поддерживается.

Порядок интерпретации скобок может привести к непредвиденным результатам. Например, рассмотрим элемент {{{0:D}}}формата, который предназначен для отображения открывающей фигурной скобки, числового значения, отформатированного в виде десятичного числа, и закрывающей фигурной скобки. Однако элемент формата интерпретируется следующим образом:

  1. Первые два открывающих фигурных скобки ({{) экранируются и дают одну открывающую скобку.
  2. Следующие три символа ({0:) интерпретируются как начало элемента формата.
  3. Следующий символ (D) будет интерпретироваться как описатель числового формата Decimal, но следующие два экранированных фигурных скобок (}}) дают одну фигурную скобку. Поскольку результирующая строка (D}) не является стандартным описатель числового формата, результирующая строка интерпретируется как строка настраиваемого формата, которая означает отображение строки D}литерала.
  4. Последняя фигурная скобка (}) интерпретируется как конец элемента формата.
  5. Конечным результатом, который отображается, является литералная строка. {D} Числовое значение, которое должно быть отформатировано, не отображается.

Один из способов написания кода, чтобы избежать неправильного интерпретации экранированных фигурных скобок и элементов форматирования, — форматирование фигурных скобок и элементов форматирования отдельно. То есть в первой операции форматирования отображается открывающая фигурная скобка литерала. В следующей операции отобразится результат элемента форматирования, а в последней операции — закрывающая скобка литерала. Следующий пример иллюстрирует этот подход:

int value = 6324;
string output = string.Format("{0}{1:D}{2}",
                             "{", value, "}");
Console.WriteLine(output);
// The example displays the following output:
//       {6324}
Dim value As Integer = 6324
Dim output As String = String.Format("{0}{1:D}{2}", _
                                     "{", value, "}")
Console.WriteLine(output)
' The example displays the following output:
'       {6324}

Порядок обработки

Если вызов метода составного форматирования содержит IFormatProvider аргумент, значение которого нет null, среда выполнения вызывает его IFormatProvider.GetFormat метод для запроса ICustomFormatter реализации. Если метод может вернуть реализацию ICustomFormatter , он кэширован на время вызова метода составного форматирования.

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

  1. Если форматируемое значение является значением null, возвращается пустая строка String.Empty.

  2. Если реализация ICustomFormatter доступна, среда выполнения вызывает ее метод Format. Среда выполнения передает значение элемента formatString формата (или null если оно отсутствует) методу. Среда выполнения также передает IFormatProvider реализацию методу. Если вызов ICustomFormatter.Format метода возвращается null, выполнение переходит к следующему шагу. В противном случае возвращается результат ICustomFormatter.Format вызова.

  3. Если значение реализует интерфейс IFormattable, вызывается метод ToString(String, IFormatProvider) этого интерфейса. Если он присутствует в элементе формата, значение formatString передается методу. null В противном случае передается. Аргумент IFormatProvider определяется следующим образом:

    • Для числового значения, если вызывается метод составного форматирования с аргументом IFormatProvider, не равным null, то среда выполнения запрашивает объект NumberFormatInfo из метода IFormatProvider.GetFormat. Если не удается указать его, если значение аргумента равно nullили если метод составного форматирования не имеет IFormatProvider параметра, NumberFormatInfo используется объект для текущего языка и региональных параметров.

    • Для значения даты и времени, если вызывается метод составного форматирования с аргументом IFormatProvider, не равным null, то среда выполнения запрашивает объект DateTimeFormatInfo из метода IFormatProvider.GetFormat. В следующих ситуациях DateTimeFormatInfo используется объект для текущего языка и региональных параметров:

    • Для объектов других типов, если метод составного форматирования вызывается с аргументом IFormatProvider, то его значение передается непосредственно в реализацию IFormattable.ToString. В противном случае null передается в реализацию IFormattable.ToString.

  4. Вызывается метод ToString без параметров, который переопределяет Object.ToString() или наследует поведение базового класса. В этом случае строка формата, указанная компонентом formatString в элементе формата, если она присутствует, игнорируется.

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

Примеры кода

В приведенном ниже примере одна строка создается с помощью составного форматирования, а другая – с помощью метода ToString. Оба способа форматирования дают идентичные результаты.

string FormatString1 = String.Format("{0:dddd MMMM}", DateTime.Now);
string FormatString2 = DateTime.Now.ToString("dddd MMMM");
Dim FormatString1 As String = String.Format("{0:dddd MMMM}", DateTime.Now)
Dim FormatString2 As String = DateTime.Now.ToString("dddd MMMM")

Предположим, что сейчас май, а текущий день недели — четверг. Тогда значение обеих строк в предыдущем примере будет Thursday May для языка и региональных параметров "Английский (США)".

Метод Console.WriteLine предоставляет те же функциональные возможности, что и метод String.Format. Единственное различие между этими двумя методами состоит в том, что метод String.Format возвращает результаты в виде строки, а метод Console.WriteLine записывает результаты в поток вывода, связанный с объектом Console. В следующем примере метод используется Console.WriteLine для форматирования MyInt значения в денежном значении:

int MyInt = 100;
Console.WriteLine("{0:C}", MyInt);
// The example displays the following output
// if en-US is the current culture:
//        $100.00
Dim MyInt As Integer = 100
Console.WriteLine("{0:C}", MyInt)
' The example displays the following output
' if en-US is the current culture:
'        $100.00

В следующем примере показано форматирование нескольких объектов, включая форматирование одного объекта двумя разными способами:

string myName = "Fred";
Console.WriteLine(String.Format("Name = {0}, hours = {1:hh}, minutes = {1:mm}",
      myName, DateTime.Now));
// Depending on the current time, the example displays output like the following:
//    Name = Fred, hours = 11, minutes = 30
Dim myName As String = "Fred"
Console.WriteLine(String.Format("Name = {0}, hours = {1:hh}, minutes = {1:mm}", _
                  myName, DateTime.Now))
' Depending on the current time, the example displays output like the following:
'    Name = Fred, hours = 11, minutes = 30                 

В следующем примере показывается использование выравнивания при форматировании. Форматируемые аргументы разделены знаками вертикальной черты ("|"), подчеркивающими полученное выравнивание.

string myFName = "Fred";
string myLName = "Opals";
int myInt = 100;
string FormatFName = String.Format("First Name = |{0,10}|", myFName);
string FormatLName = String.Format("Last Name = |{0,10}|", myLName);
string FormatPrice = String.Format("Price = |{0,10:C}|", myInt);
Console.WriteLine(FormatFName);
Console.WriteLine(FormatLName);
Console.WriteLine(FormatPrice);
Console.WriteLine();

FormatFName = String.Format("First Name = |{0,-10}|", myFName);
FormatLName = String.Format("Last Name = |{0,-10}|", myLName);
FormatPrice = String.Format("Price = |{0,-10:C}|", myInt);
Console.WriteLine(FormatFName);
Console.WriteLine(FormatLName);
Console.WriteLine(FormatPrice);
// The example displays the following output on a system whose current
// culture is en-US:
//          First Name = |      Fred|
//          Last Name = |     Opals|
//          Price = |   $100.00|
//
//          First Name = |Fred      |
//          Last Name = |Opals     |
//          Price = |$100.00   |
Dim myFName As String = "Fred"
Dim myLName As String = "Opals"

Dim myInt As Integer = 100
Dim FormatFName As String = String.Format("First Name = |{0,10}|", myFName)
Dim FormatLName As String = String.Format("Last Name = |{0,10}|", myLName)
Dim FormatPrice As String = String.Format("Price = |{0,10:C}|", myInt)
Console.WriteLine(FormatFName)
Console.WriteLine(FormatLName)
Console.WriteLine(FormatPrice)
Console.WriteLine()

FormatFName = String.Format("First Name = |{0,-10}|", myFName)
FormatLName = String.Format("Last Name = |{0,-10}|", myLName)
FormatPrice = String.Format("Price = |{0,-10:C}|", myInt)
Console.WriteLine(FormatFName)
Console.WriteLine(FormatLName)
Console.WriteLine(FormatPrice)
' The example displays the following output on a system whose current
' culture is en-US:
'          First Name = |      Fred|
'          Last Name = |     Opals|
'          Price = |   $100.00|
'
'          First Name = |Fred      |
'          Last Name = |Opals     |
'          Price = |$100.00   |

См. также