Skip to content

Latest commit

 

History

History
2142 lines (1893 loc) · 212 KB

c22.md

File metadata and controls

2142 lines (1893 loc) · 212 KB

ГЛАВА 22. Строки и форматирование

В этой главе рассматривается класс String, положен­ ный в основу встроенного в C# типа string. Как из­ вестно, обработка символьных строк является неот­ ъемлемой частью практически всех программ. Именно по этой причине в классе String определяется обширный ряд методов, свойств и полей, обеспечивающих наиболее полное управление процессом построения символьных строк и манипулирования ими. С обработкой строк тесно связано форматирование данных в удобочитаемой форме. Используя подсистему форматирования, можно отфор­ матировать данные всех имеющихся в C# числовых типов, а также дату, время и перечисления.

Строки в C#

Вопросы обработки строк уже обсуждались в главе 7, и поэтому не стоит повторяться. Вместо этого целесообраз­ но дать краткий обзор реализации символьных строк в С#, прежде чем переходить к рассмотрению класса String.

Во всех языках программирования строка представляет собой последовательность символов, но конкретная ее реа­ лизация отличается в разных языках. В некоторых языках программирования, например в C++, строки представля­ ют собой массивы символов, тогда как в C# они являются объектами встроенного типа данных string. Следова­ тельно, string является ссылочным типом. Более того, string — это имя стандартного для среды .NET строково­ го типа System.String. Это означает, что в C# строке как объекту доступны все методы, свойства, поля и операторы, определенные в классе String.

После создания строки последовательность составляющих ее символов не может быть изменена. Благодаря этому ограничению строки реализуются в C# более эффек­ тивно. И хотя такое ограничение кажется на первый взгляд серьезным препятствием, на самом деле оно таковым не является. Когда требуется получить строку как разно­ видность уже существующей строки, достаточно создать новую строку, содержащую требующиеся изменения, и "отвергнуть" исходную строку, если она больше не нужна. А поскольку ненужные строковые объекты автоматически утилизируются средствами "сборки мусора", то беспокоиться о дальнейшей судьбе "отвергнутых" строк не при­ ходится. Следует, однако, подчеркнуть, что переменные ссылок на строки могут, безу­ словно, изменить объект, на который они ссылаются. Но сама последовательность сим­ волов в конкретном строковом объекте не подлежит изменению после его создания.

Для создания строк, которые нельзя изменить, в C# предусмотрен класс StringBuilder, находящийся в пространстве имен System.Text. Но на практике для этой цели чаще используется тип string, а не класс StringBuilder.

Класс String

Класс String определен в пространстве имен System. В нем реализуются следую­ щие интерфейсы: IComparable, IComparable, ICloneable, IConvertible, IEnumerable, IEnumerable и IEquatable. Кроме того, String — герметичный класс, а это означает, что он не может наследоваться. В классе String предоставляются все необходимые функциональные возможности для обработки сим­ вольных строк в С#. Он служит основанием для встроенного в C# типа string и явля­ ется составной частью среды .NET Framework. В последующих разделах представлено подробное описание класса String.

Конструкторы класса String

В классе String определено несколько конструкторов, позволяющих создавать строки самыми разными способами. Для создания строки из символьного массива служит один из следующих конструкторов.

public String(char[ ] value)
public String(char[ ] value, int startIndex, int length)

Первая форма конструктора позволяет создать строку, состоящую из символов мас­ сива value. А во второй форме для этой цели из массива value извлекается опреде­ ленное количество символов (length), начиная с элемента, указываемого по индексу startIndex.

С помощью приведенного ниже конструктора можно создать строку, состоящую из отдельного символа, повторяющегося столько раз, сколько потребуется:

public String(char с, int count)

где с обозначает повторяющийся символ; a count — количество его повторений.

Кроме того, строку можно создать по заданному указателю на символьный массив, используя один из следующих конструкторов.

public String(char* value)
public String(char* value, int startIndex, int length)

Первая форма конструктора позволяет создать строку из символов, доступных из массива по указателю value. При этом предполагается, что массив, доступный по указателю value, завершается пустым символом, обозначающим конец строки. А во второй форме конструктора для этой цели из массива, доступного по указателю value, извлекается определенное количество символов (length), начиная с элемента, указываемого по индексу startIndex. В этих конструкторах применяются указатели, поэтому их можно использовать только в небезопасном коде.

И наконец, строку можно построить по заданному указателю на байтовый массив, используя один из следующих конструкторов.

public String(sbyte* value)
public String(sbyte* value, int startIndex, int length)
public String(sbyte* value, int startIndex, int length, Encoding enc)

Первая форма конструктора позволяет построить строку из отдельных байтов сим­ волов, доступных из массива по указателю value. При этом предполагается, что мас­ сив, доступный по указателю value, завершается признаком конца строки. Во второй форме конструктора для этой цели из массива, доступного по указателю value, из­ влекается определенное количество байтов символов (length), начиная с элемента, указываемого по индексу startIndex. А третья форма конструктора позволяет ука­ зать количество кодируемых байтов. Класс Encoding находится в пространстве имен System.Text. В этих конструкторах применяются указатели, и поэтому их можно ис­ пользовать только в небезопасном коде.

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

string str = "новая строка";

Поле, индексатор и свойство класса String

В классе String определено единственное поле.

public static readonly string Empty

Поле Empty обозначает пустую строку, т.е. такую строку, которая не содержит сим­ волы. Этим оно отличается от пустой ссылки типа String, которая просто делается на несуществующий объект.

Помимо этого, в классе String определен единственный индексатор, доступный только для чтения.

public char this[int index] { get; }

Этот индексатор позволяет получить символ по указанному индексу. Индексация строк, как и массивов, начинается с нуля. Объекты типа String отличаются постоян­ ством и не изменяются, поэтому вполне логично, что в классе String поддерживается индексатор, доступный только для чтения.

И наконец, в классе String определено единственное свойство, доступное только для чтения.

public int Length { get; }

Свойство Length возвращает количество символов в строке.

Операторы класса String

В классе String перегружаются два следующих оператора: == и !=. Оператор == служит для проверки двух символьных строк на равенство. Когда оператор == применя­ ется к ссылкам на объекты, он обычно проверяет, делаются ли обе ссылки на один и тот же объект. А когда оператор == применяется к ссылкам на объекты типа String, то на предмет равенства сравнивается содержимое самих строк. Это же относится и к операто­ ру !=. Когда он применяется к ссылкам на объекты типа String, то на предмет неравен­ ства сравнивается содержимое самих строк. В то же время другие операторы отношения, в том числе < и >=, сравнивают ссылки на объекты типа String таким же образом, как и на объекты других типов. А для того чтобы проверить, является ли одна строка больше другой, следует вызвать метод Compare(), определенный в классе String.

Как станет ясно дальше, во многих видах сравнения символьных строк используют­ ся сведения о культурной среде. Но это не относится к операторам = = и !=. Ведь они просто сравнивают порядковые значения символов в строках. (Иными словами, они сравнивают двоичные значения символов, не видоизмененные нормами культурной среды, т.е. региональными стандартами.) Следовательно, эти операторы выполняют сравнение строк без учета регистра и настроек культурной среды.

Сравнение строк

Вероятно, из всех операций обработки символьных строк чаще всего выполняется сравнение одной строки с другой. Прежде чем рассматривать какие-либо методы срав­ нения строк, следует подчеркнуть следующее: сравнение строк может быть выполнено в среде .NET Framework двумя основными способами. Во-первых, сравнение может от­ ражать обычаи и нормы отдельной культурной среды, которые зачастую представляют собой настройки культурной среды, вступающие в силу при выполнении программы. Это стандартное поведение некоторых, хотя и не всех методов сравнения. И во-вторых, сравнение может быть выполнено независимо от настроек культурной среды только по порядковым значениям символов, составляющих строку. Вообще говоря, при срав­ нении строк без учета культурной среды используется лексикографический порядок (и лингвистические особенности), чтобы определить, является ли одна строка больше, меньше или равной другой строке. При порядковом сравнении строки просто упоря­ дочиваются на основании невидоизмененного значения каждого символа.

ПРИМЕЧАНИЕ В силу отличий способов сравнения строк с учетом культурной среды и порядкового срав­ нения, а также последствий каждого такого сравнения настоятельно рекомендуется руковод­ ствоваться лучшими методиками, предлагаемыми в настоящее время корпорацией Microsoft. Ведь выбор неверного способа сравнения строк может привести к неправильной работе про­ граммы, когда она эксплуатируется в среде, отличающей от той, в которой она разработана.

Выбор способа сравнения символьных строк представляет собой весьма ответствен­ ное решение. Как правило и без всякий исключений, следует выбирать сравнение строк с учетом культурной среды, если это делается для целей отображения результа­ та пользователю (например, для вывода на экран ряда строк, отсортированных в лек­ сикографическом порядке). Но если строки содержат фиксированную информацию, не предназначенную для видоизменения с учетом отличий в культурных средах, на­ пример, имя файла, ключевое слово, адрес веб-сайта иди значение, связанное с обе­ спечением безопасности, то следует выбрать порядковое сравнение строк. Разумеется, особенности конкретного разрабатываемого приложения будут диктовать выбор под­ ходящего способа сравнения символьных строк.

В классе String предоставляются самые разные методы сравнения строк, перечис­ ленные в табл. 22.1. Наиболее универсальным среди них является метод Compare(). Он позволяет сравнивать две строки полностью или частично, с учетом или без учета регистра, способа сравнения, определяемого параметром типа StringComparison, а также сведений о культурной среде, предоставляемых с помощью параметра типа CultureInfo. Те перегружаемые варианты метода Compare(), которые не содержат параметр типа StringComparison, выполняют сравнение символьных строк с учетом регистра и культурной среды. А в тех перегружаемых его вариантах, которые не со­ держат параметр типа CultureInfo, сведения о культурной среде определяются теку­ щей средой выполнения. В примерах программ, приведенных в этой главе, параметр типа CultureInfo не используется, а большее внимание уделяется использованию параметра типа StringComparison.

Таблица 22.1. Методы сравнения символьных строк

Метод Назначение
public static int Compare(string strA, string strB) Сравнивает строку strA со строкой strB. Возвращает положительное значение, если строка strA больше строки strB; отрицательное значение, если строка strA меньше строки strB; и нуль, если строки strA и strB равны. Сравнение выполняется с учетом регистра и культурной среды
public static int Compare(string strA, string strB, bool ignoreCase) Сравнивает строку strA со строкой strB. Возвращает положительное значение, если строка strA больше строки strB; отрицательное значение, если строка strA меньше строки strB; и нуль, если строки strA и strB равны. Если параметр ignoreCase принимает логическое значение true, то при сравнении не учитываются различия между прописным и строчным вариантами букв. В противном случае эти различия учитываются. Сравнение выполняется с учетом культурной среды
public static int Compare(string strA, string strB, StringComparison comparisonType) Сравнивает строку strA со строкой strB. Возвращает положительное значение, если строка strA больше строки strB; отрицательное значение, если строка strA меньше строки strB; и нуль, если строки strA и strB равны. Параметр comparisonType определяет конкретный способ сравнения строк
public static int Compare(string strA, string strB, bool ignoreCase, Culturelnfo culture) Сравнивает строку strA со строкой strB, используя информацию о культурной среде, определяемую параметром culture. Возвращает положительное значение, если строка strA больше строки strB; отрицательное значение, если строка strA меньше строки strB; и нуль, если строки strA и strB равны. Если параметр ignoreCase принимает логическое значение true, то при сравнении не учитываются различия между прописным и строчным вариантами букв. В противном случае эти различия учитываются. Класс CultureInfo определен в пространстве имен System.Globalization
public static int Compare(string strA, int indexA, string strB, int indexB, int length) Сравнивает части строк strA и strB. Сравнение начинается со строковых элементов strA[indexA] и strB[indexB] и включает количество символов, определяемых параметром length. Метод возвращает положительное значение, если часть строки strA больше части строки strB; отрицательное значение, если часть строки strA меньше части строки strB; и нуль, если сравниваемые части строк strA и strB равны. Сравнение выполняется с учетом регистра и культурной среды
public static int Compare(string strA, int IndexA, string strB, int indexB, int length, bool ignoreCase) Сравнивает части строк strA и strB. Сравнение начинается со строковых элементов strA[indexA] и strB[indexB] и включает количество символов, определяемых параметром length. Метод возвращает положительное значение, если часть строки strA больше части строки strB; отрицательное значение, если часть строки strA меньше части строки strB; и нуль, если сравниваемые части строк strA и strB равны. Если параметр ignoreCase принимает логическое значение true, то при сравнении не учитываются различия между прописным и строчным вариантами букв. В противном случае эти различия учитываются. Сравнение выполняется с учетом культурной среды
public static int Compare(string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType) Сравнивает части строк strA и strB. Сравнение начинается со строковых элементов strA[indexA] и strB[indexB] и включает количество символов, определяемых параметром length. Метод возвращает положительное значение, если часть строки strA больше части строки strB; отрицательное значение, если часть строки strA меньше части строки strB; и нуль, если сравниваемые части строк strA и strB равны. Параметр comparisonType определяет конкретный способ сравнения строк
public static int Compare(string strA, int indexA, string strB, int indexB, int length, bool ignoreCase, CultureInfo culture) Сравнивает части строк strA и strB, используя инфор­ мацию о культурной среде, определяемую параметром culture. Сравнение начинается со строковых элементов strA[indexA] и strB[indexB] и включает количество символов, определяемых параметром length. Метод возвращает положительное значение, если часть строки strA больше части строки strB; отрицательное значение, если часть строки strA меньше части строки strB; и нуль, если сравниваемые части строк strA и strB равны. Если параметр ignoreCase принимает логическое значение true, то при сравнении не учитываются различия между прописным и строчным вариантами букв. В противном случае эти различия учитываются. Класс CultureInfo определен в пространстве имен System.Globalization
public static int Compare(string strA, string strB, Culturelnfo culture, CompareOptions options) Сравнивает строку strA со строкой strB, используя информацию о культурной среде, обозначаемую параметром culture, а также варианты сравнения, передаваемые в качестве параметра options. Возвращает положительное значение, если строка strA больше строки strB; отрицательное значение, если строка strA меньше строки strB; и нуль, если строки strA и strB равны. Классы CultureInfo и CompareOptions определены в пространстве имен System.Globalization
public static int Compare(string strA, int indexA, string strB, int indexB, int length, CultureInfo culture, CompareOptions options) Сравнивает части строк strA и strB, используя информацию о культурной среде, обозначаемую параметром culture, а также варианты сравнения, передаваемые в качестве параметра options. Сравнение начинается со строковых элементов strA[indexA] и strB[indexB] и включает количество символов, определяемых параметром length. Метод возвращает положительное значение, если часть строки strA больше части строки strB; отрицательное значение, если часть строки strA меньше части строки strB; и нуль, если сравниваемые части строк strA и strB равны. Классы CultureInfo и CompareOptions определены в пространстве имен System.Globalization
public static int CompareOrdinal(string strA, string strB) Сравнивает строку strA со строкой strB независимо от культурной среды, языка и региональных стандартов. Возвращает положительное значение, если строка strA больше строки strB; отрицательное значение, если строка strA меньше строки strB; и нуль, если строки strA и strB равны
public static int CompareOrdinal(string strA, int indexA, string strB, int IndexB, int count) Сравнивает части строк strA и strB независимо от культурной среды, языка и региональных стандартов. Сравнение начинается со строковых элементов strA[indexA] и strB[indexB] и включает количество символов, определяемых параметром count. Метод возвращает положительное значение, если часть строки strA больше части строки strB; отрицательное значение, если часть строки strA меньше части строки strB; и нуль, если сравниваемые части строк strA и strB равны
public int CompareTo(object value) Сравнивает вызывающую строку со строковым представлением объекта value. Возвращает положительное значение, если вызывающая строка больше строки value; отрицательное значение, если вызывающая строка меньше строки value; и нуль, если сравниваемые строки равны
public int CompareTo(string strB) Сравнивает вызывающую строку со строкой strB. Возвращает положительное значение, если вызывающая строка больше строки strB; отрицательное значение, если вызывающая строка меньше строки strB; и нуль, если сравниваемые строки равны
public override bool Equals(object obj) Возвращает логическое значение true, если вызывающая строка содержит ту же последовательность символов, что и строковое представление объекта obj. Выполняется порядковое сравнение с учетом регистра, но без учета культурной среды
public bool Equals(string value) Возвращает логическое значение true, если вызывающая строка содержит ту же последовательность символов, что и строка value. Выполняется порядковое сравнение с учетом регистра, но без учета культурной среды
public bool Equals(string value, StringComparison comparisonType) Возвращает логическое значение true, если вызывающая строка содержит ту же последовательность символов, что и строка value. Параметр comparisonType определяет конкретный способ сравнения строк
public static bool Equals(string a, string b) Возвращает логическое значение true, если строка а содержит ту же последовательность символов, что и строка b. Выполняется порядковое сравнение с учетом регистра, но без учета культурной среды
public static bpol Equals(string a, string b, StringComparison comparisonType) Возвращает логическое значение true, если строка а содержит ту же последовательность символов, что и строка b. Параметр comparisonType определяет конкретный способ сравнения строк

Тип StringComparison представляет собой перечисление, в котором определяются значения, приведенные в табл. 22.2. Используя эти значения, можно организовать срав­ нение строк, удовлетворяющее потребностям конкретного приложения. Следователь­ но, добавление параметра типа StringComparison расширяет возможности метода Compare() и других методов сравнения, например, Equals(). Это дает также возмож­ ность однозначно указывать способ предполагаемого сравнения строк. В силу имеющих отличий между сравнением строк с учетом культурной среды и порядковым сравнени­ ем очень важно быть предельно точным в этом отношении. Именно по этой причине в примерах программ, приведенных в данной книге, параметр типа StringComparison явно указывается в вызовах тех методов, в которых он поддерживается.

Таблица 22.2. Значения, определяемые в перечислении StringComparison

Значение Описание
CurrentCulture Сравнение строк производится с использованием текущих настроек параметров культурной среды
CurrentCultureIgnoreCase Сравнение строк производится с использованием текущих настроек параметров культурной среды, но без учета регистра
InvariantCulture Сравнение строк производится с использованием неизменяемых, т.е. универсальных данных о культурной среде
InvariantCultureIngoreCase Сравнение строк производится с использованием неизменяемых, т.е. универсальных данных о культурной среде и без учета регистра
Ordinal Сравнение строк производится с использованием порядковых значений символов в строке. При этом лексикографический порядок может нарушиться, а условные обозначения, принятые в отдельной культурной среде, игнорируются
OrdinalIgnoreCase Сравнение строк производится с использованием порядковых значений символов в строке, но без учета регистра. При этом лексикографический порядок может нарушиться, а условные обозначения, принятые в отдельной культурной среде, игнорируются

В любом случае метод Compare() возвращает отрицательное значение, если первая сравниваемая строка оказывается меньше второй; положительное значение, если пер­ вая сравниваемая строка больше второй; и наконец, нуль, если обе сравниваемые стро­ ки равны. Несмотря на то что метод Compare() возвращает нуль, если сравниваемые строки равны, для определения равенства символьных строк, как правило, лучше поль­ зоваться методом Equals() иди же оператором = =. Дело в том, что метод Compare() определяет равенство сравниваемых строк на основании порядка их сортировки. Так, если выполняется сравнение строк с учетом культурной среды, то обе строки могут оказаться одинаковыми по порядку их сортировки, но не равными по существу. По умолчанию равенство строк определяется в методе Equals(), исходя из порядковых значений символов и без учета культурной среды. Следовательно, по умолчанию обе строки сравниваются в этом методе на абсолютное, посимвольное равенство подобно тому, как это делается в операторе = =.

Несмотря на большую универсальность метода Compare(), для простого порядко­ вого сравнения символьных строк проще пользоваться методом CompareOrdinal(). И наконец, следует иметь в виду, что метод CompareTo() выполняет сравнение строк только с учетом культурной среды. На момент написания этой книги отсутствовали перегружаемые варианты этого метода, позволявшие указывать другой способ срав­ нения символьных строк.

В приведенной ниже программе демонстрируется применение методов Compare(), Equals(), CompareOrdinal(), а также операторов = = и != для сравнения символь­ ных строк. Обратите внимание на то, что два первых примера сравнения наглядно де­ монстрируют отличия между сравнением строк с учетом культурной среды и поряд­ ковым сравнением в англоязычной среде.

// Продемонстрировать разные способы сравнения символьных строк.
using System;

class CompareDemo {
	static void Main() {
		string str1 = "alpha";
		string str2 = "Alpha";
		string str3 = "Beta";
		string str4 = "alpha";
		string str5 = "alpha, beta";
		int result;

		// Сначала продемонстрировать отличия между сравнением строк
		// с учетом культурной среды и порядковым сравнением.
		result = String.Compare(str1, str2, StringComparison.CurrentCulture)
		Console.Write("Сравнение строк с учетом культурной среды: ");
		if(result < 0)
			Console.WriteLine(str1 + " меньше " + str2);
		else if(result > 0)
			Console.WriteLine(str1 + " больше " + str2);
		else
			Console.WriteLine(str1 + " равно " + str2);

		result = String.Compare(str1, str2, StringComparison.Ordinal);
		Console.Write("Порядковое сравнение строк: ");
		if(result < 0)
			Console.WriteLine(str1 + " меньше " + str2);
		else if(result > 0)
			Console.WriteLine(str1 + " больше " + str2);
		else
			Console.WriteLine(str1 + " равно " + str4);

		// Использовать метод CompareOrdinal().
		result = String.CompareOrdinal(str1, str2);
		Console.Write("Сравнение строк методом CompareOrdinal():\n");
		if(result < 0)
			Console.WriteLine(str1 + " меньше " + str2);
		else if(result > 0)
			Console.WriteLine(str1 + " больше " + str2);
		else
			Console.WriteLine(str1 + " равно " + str4);

		Console.WriteLine();

		// Определить равенство строк о помощью оператора = = .
		// Это порядковое сравнение символьных строк.
		if(str1 == str4) Console.WriteLine(str1 + " == " + str4);

		// Определить неравенство строк с помощью оператора !=.
		if(str1 != str3) Console.WriteLine(str1 + " != " + str3);
		if(str1 != str2) Console.WriteLine(str1 + " != " + str2);

		Console.WriteLine();

		// Выполнить порядковое сравнение строк без учета регистра,
		// используя метод Equals().
		if(String.Equals(str1, str2, StringComparison.OrdinalIgnoreCase))
			Console.WriteLine("Сравнение строк методом Equals() с " +
							"параметром OrdinalIgnoreCase:\n" +
							str1 + " равно " + str2);
		Console.WriteLine ();

		// Сравнить части строк.
		if(String.Compare(str2, 0, str5, 0, 3,
		StringComparison.CurrentCulture) > 0) {
			Console.WriteLine("Сравнение строк с учетом текущей культурной среды:" +
							"\n3 первых символа строки " + str2 +
							" больше, чем 3 первых символа строки " + str5);
		}
	}
}

Выполнение этой программы приводит к следующему результату.

Сравнение строк с учетом культурной среды: alpha меньше Alpha
Порядковое сравнение строк: alpha больше Alpha
Сравнение строк методом CompareOrdinal():
alpha больше Alpha

alpha == alpha
alpha != Beta
alpha != Alpha

Сравнение строк методом Equals() с параметром OrdinalIgnoreCase:
alpha равно Alpha

Сравнение строк с учетом текущей культурной среды:
3 первых символа строки Alpha больше, чем 3 первых символа строки alpha, beta

Сцепление строк

Строки можно сцеплять, т.е. объединять вместе, двумя способами. Во-первых, с по­ мощью оператора +, как было показано в главе 7. И во-вторых, с помощью одного из методов сцепления, определенных в классе String. Конечно, для этой цели проще всего воспользоваться оператором +, тем не менее методы сцепления служат неплохой альтернативой такому подходу.

Метод, выполняющий сцепление строк, называется Concat(). Ниже приведена одна из самых распространенных его форм.

public static string Concat(string str0, string str1)

Этот метод возвращает строку, состоящую из строки str1, присоединяемой путем сцепления в конце строки str0. Ниже приведена еще одна форма метода Concat(), в которой сцепляются три строки.

public static string Concat(string str0, string str1,string str2)

В данной форме метод Concat() возвращает строку, состоящую из последователь­ но сцепленных строк str0, str1 и str2.

Имеется также форма метода Concat(), в которой сцепляются четыре строки.

public static string Concat(string str0, string str1, string str2, string str3)

В этой форме метод Concat() возвращает строку, состоящую из четырех последо­ вательно сцепленных строк.

А в приведенной ниже еще одной форме метода Concat() сцепляется произволь­ ное количество строк:

public static string Concat(params string[] values)

где values обозначает переменное количество аргументов, сцепляемых для получе­ ния возвращаемого результата. Если в этой форме метода Concat() допускается сце­ пление произвольного количества строк, то зачем нужны все остальные его формы? Они существуют ради повышения эффективности. Ведь передача методу от одного до четырех аргументов оказывается намного эффективнее, чем использование для этой цели переменного списка аргументов.

В приведенном ниже примере программы демонстрируется применение метода Concat() в форме с переменным списком аргументов.

// Продемонстрировать применение метода Concat().
using System;

class ConcatDemo {
	static void Main() {
		string result = String.Concat("Это ", "тест ", "метода ",
			"сцепления ", "строк ",
			"из класса ", "String." );
		Console.WriteLine("Результат: " + result);
	}
}

Эта программа дает следующий результат.

Результат: Это тест метода сцепления строк из класса String.

Кроме того, существуют варианты метода Concat(), в которых он принима­ ет в качестве параметров ссылки на объекты, а не на строки. В этих вариантах метод Concat() получает строковые представления вызывающих объектов, а возвращает объединенную строку, сцепленную из этих представлений. (Строковые представления объектов получаются с помощью метода ToString(), вызываемого для этих объек­ тов.) Ниже приведены все подобные варианты и формы метода Concat().

public static string Concat(object arg0)
public static string Concat(object arg0, object arg1)
public static string Concat(object arg0, object arg1, object arg2)
public static string Concat(object arg0, object arg1, object arg2, object arg3)
public static string Concat(params object[] args)

В первой форме метод Concat() возвращает строку, эквивалентную объекту arg0, а в остальных формах — строку, получаемую в результате сцепления всех аргументов данного метода. Объектные формы метода Concat(), т.е. относящиеся к типу object, очень удобны, поскольку они исключают получение вручную строковых представле­ ний объектов перед их сцеплением. В приведенном ниже примере программы нагляд­ но демонстрируется польза от подобных форм метода Concat().

// Продемонстрировать применение объектной формы метода Concat().
using System;

class MyClass {
	public static int Count = 0;
	public MyClass() { Count++; }
}

class ConcatDemo {
	static void Main() {
		string result = String.Concat("значение равно " + 19);
		Console.WriteLine("Результат: " + result);

		result = String.Concat("привет ", 88, " ", 20.0,
							" ", false, " ", 23.45M);
		Console.WriteLine("Результат: " + result);

		MyClass me = new MyClass();

		result = String.Concat(me, " текущий счет равен ",
							MyClass.Count);
		Console.WriteLine("Результат: " + result);
	}
}

Вот к какому результату приводит выполнение этой программы.

Результат: значение равно 19
Результат: привет 88 20 False 23.45
Результат: MyClass текущий счет равен 1

В данном примере метод Concat() сцепляет строковые представления различных типов данных. Для каждого аргумента этого метода вызывается соответствующий ме­ тод ToString(), с помощью которого получается строковое представление аргумента. Следовательно, в следующем вызове метода Concat():

string result = String.Concat("значение равно " + 19);

метод Int32.ToString() вызывается для получения строкового представления цело­ го значения 19, а затем метод Concat() сцепляет строки и возвращает результат.

Обратите также внимание на применение объекта определяемого пользователем класса MyClass в следующем вызове метода Concat().

result = String.Concat(me, " текущий счет равен ",
					MyClass.Count);

В данном случае возвращается строковое представление объекта типа MyClass, сцепленное с указываемой строкой. По умолчанию это просто имя класса. Но если переопределить метод ToString(), то вместо строки с именем класса MyClass может быть возвращена другая строка. В качестве упражнения попробуйте ввести в приведен­ ный выше пример программы следующий фрагмент кода.

public override string ToString() {
	return "Объект типа MyClass";
}

В этом случае последняя строка результата выполнения программы будет выгля­ деть так, как показано ниже.

Результат: Объект типа MyClass текущий счет равен 1

В версию 4.0 среды .NET Framework добавлены еще две формы метода Concat(), приведенные ниже.

public static string Concat<T>(IEnumerable<T> values)
public static string Concat(IEnumerable<string> values)

В первой форме этого метода возвращается символьная строка, состоящая из сце­ пленных строковых представлений ряда значений, имеющихся в объекте, который обозначается параметром values и может быть объектом любого типа, реализующего интерфейс IEnumerable<T>. А во второй форме данного метода сцепляются строки, обозначаемые параметром values. (Следует, однако, иметь в виду, что если прихо­ дится выполнять большой объем операций сцепления символьных строк, то для этой цели лучше воспользоваться средствами класса StringBuilder.)

Поиск в строке

В классе String предоставляется немало методов для поиска в строке. С их помо­ щью можно, например, искать в строке отдельный символ, строку, первое или послед­ нее вхождение того и другого в строке. Следует, однако, иметь в виду, что поиск может осуществляться либо с учетом культурной среды либо порядковым способом.

Для обнаружения первого вхождения символа или подстроки в исходной стро­ ке служит метод IndexOf(). Для него определено несколько перегружаемых форм. Ниже приведена одна из форм для поиска первого вхождения символа в исходной строке.

public int IndexOf(char value)

В этой форме метода IndexOf() возвращается первое вхождение символа value в вызывающей строке. Если символ value в ней не найден, то возвращается значение -1. При таком поиске символа настройки культурной среды игнорируются. Следователь­ но, в данном случае осуществляется порядковый поиск первого вхождения символа.

Ниже приведены еще две формы метода IndexOf(), позволяющие искать первое вхождение одной строки в другой.

public int IndexOf(String value)
public int IndexOf(String value, StringComparison comparisonType)

В первой форме рассматриваемого здесь метода поиск первого вхождения строки, обозначаемой параметром value, осуществляется с учетом культурной среды. А во вто­ рой форме предоставляется возможность указать значение типа StringComparison, обозначающее способ поиска. В если искомая строка не найдена, то в обеих формах данного метода возвращается значение -1.

Для обнаружения последнего вхождения символа или строки в исходной строке служит метод LastIndexOf(). И для этого метода определено несколько перегружае­ мых форм. Ниже приведена одна из форм для поиска последнего вхождения символа в вызывающей строке.

public int LastlndexOf(char value)

В этой форме метода LastIndexOf() осуществляется порядковый поиск, а в итоге возвращается последнее вхождение символа value в вызывающей строке или же зна­ чение -1, если искомый символ не найден.

Ниже приведены еще две формы метода LastIndexOf(), позволяющие искать по­ следнее вхождение одной строки в другой.

public int LastlndexOf(string value)
public int LastlndexOf(string value, StringComparison comparisonType)

В первой форме рассматриваемого здесь метода поиск последнего вхожде­ ния строки, обозначаемой параметром value, осуществляется с учетом культур­ ной среды. А во второй форме предоставляется возможность указать значение типа StringComparison, обозначающее способ поиска. Если же искомая строка не найде­ на, то в обеих формах данного метода возвращается значение -1.

В классе String предоставляются еще два интересных метода поиска в строке: IndexOfAny() и LastIndexOfAny(). Оба метода обнаруживают первый символ, со­ впадающий с любым набором символов. Ниже приведены простейшие формы этих методов.

public int IndexOfAny(char[] anyOf)
public int LastIndexOfAny(char[] anyOf)

Метод IndexOfAny() возвращает индекс первого вхождения любого символа из массива anyOf, обнаруженного в вызывающей строке, а метод LastIndexOfAny() — индекс последнего вхождения любого символа из массива anyOf, обнаруженного в вы­ зывающей строке. Если совпадение символов не обнаружено, то в обоих случаях воз­ вращается значение -1. Кроме того, в обоих рассматриваемых здесь методах осущест­ вляется порядковый поиск.

При обработке символьных строк нередко оказывается полезно знать, начинается ли строка заданной подстрокой или же оканчивается ею. Для этой цели служат мето­ ды StartsWith() и EndsWith(). Ниже приведены их простейшие формы.

public bool StartsWith(string value)
public bool EndsWith(string value)

Метод StartsWith() возвращает логическое значение true, если вызывающая строка начинается с подстроки, переданной ему в качестве аргумента value. А метод EndsWith() возвращает логическое значение true, если вызывающая строка оканчи­ вается подстрокой, переданной ему в качестве аргумента value. В противном случае оба метода возвращают логическое значение false.

В обоих рассматриваемых здесь методах поиск осуществляется с учетом культурной среды. Для того чтобы указать конкретный способ поиска подстроки, можно восполь­ зоваться приведенными ниже вариантами этих методов с дополнительным параме­ тром типа StringComparison.

public bool StartsWith(string value, StringComparison comparisonType)
public bool EndsWith(string value, StringComparison comparisonType)

Оба варианта рассматриваемых здесь методов поиска действуют таким же образом, как и предыдущие их варианты. Но в то же время они позволяют явно указать кон­ кретный способ поиска.

В приведенном ниже примере программы демонстрируется применение несколь­ ких методов поиска в строке.

// Продемонстрировать поиск в строке.
using System;

class StringSearchDemo {
	static void Main() {
		string str = "C# обладает эффективными средствами обработки строк.";
		int idx;

		Console.WriteLine("Строка str: " + str);

		idx = str.IndexOf('о');
		Console.WriteLine("Индекс первого вхождения символа 'o': " + idx);

		idx = str.LastIndexOf('о');
		Console.WriteLine("Индекс последнего вхождения символа 'o': " + idx);

		idx = str.IndexOf("ми", StringComparison.Ordinal);
		Console.WriteLine("Индекс первого вхождения подстроки \"ми\": " + idx);

		idx = str.LastlndexOf("ми", StringComparison.Ordinal);
		Console.WriteLine("Индекс последнего вхождения подстроки \"ми\": " + idx);

		char[] chrs = { 'а', 'б', 'в' };
		idx = str.IndexOfAny(chrs);
		Console.WriteLine("Индекс первого вхождения символов " +
						" 'а', 'б' или 'в': " + idx);

		if(str.StartsWith("C# обладает", StringComparison.Ordinal))
			Console.WriteLine("Строка str начинается с подстроки \"C# обладает!"");

		if(str.EndsWith("строк.", StringComparison.Ordinal))
			Console.WriteLine("Строка str оканчивается подстрокой \"строк.\"");
	}
}

Ниже приведен результат выполнения этой программы.

Строка str: C# обладает эффективными средствами обработки строк.
Индекс первого вхождения символа 'о': 3
Индекс последнего вхождения символа 'о': 49
Индекс первого вхождения подстроки "ми": 22
Индекс последнего вхождения подстроки "ми": 33
Индекс первого вхождения символов 'а', 'б' или 'в': 4
Строка str начинается с подстроки "C# обладает"
Строка str оканчивается подстрокой "строк."

Во многих случаях полезным для поиска в строке оказывается метод Contains(). Его общая форма выглядит следующим образом.

public bool Contains(string value)

Метод Contains() возвращает логическое значение true, если вызывающая стро­ ка содержит подстроку, обозначаемую параметром value, в противном случае — ло­ гическое значение false. Поиск указываемой подстроки осуществляется порядковым способом. Этот метод особенно полезен, если требуется только выяснить, находится ли конкретная подстрока в другой строке. В приведенном ниже примере программы демонстрируется применение метода Contains().

// Продемонстрировать применение метода Contains().
using System;

class ContainsDemo {
	static void Main() {
		string str = "C# сочетает эффективность с производительностью.";
		if(str.Contains("эффективность"))
			Console.WriteLine("Обнаружена подстрока \"эффективность\".");

		if(str.Contains("эффе"))
			Console.WriteLine("Обнаружена подстрока \"эффе\".");

		if(!str.Contains("эффективный"))
			Console.WriteLine("Подстрока \"эффективный!" не обнаружена.");
	}
}

Выполнение этой программы приводит к следующему результату.

Обнаружена подстрока "эффективность".
Обнаружена подстрока "эффе".
Подстрока "эффективный" не обнаружена.

Как следует из результата выполнения приведенной выше программы, метод Contains() осуществляет поиск на совпадение произвольной последовательности символов, а не только целых слов. Поэтому в вызывающей строке обнаруживается и подстрока "эффективность", и подстрока "эффе". Но поскольку в вызывающей стро­ ке отсутствует подстрока "эффективный", то она и не обнаруживается.

У некоторых методов поиска в строке имеются дополнительные формы, позволяю­ щие начинать поиск по указанному индексу или указывать пределы для поиска в стро­ ке. В табл. 22.3 сведены все варианты методов поиска в строке, которые поддерживают­ ся в классе String.

Таблица 22.3. Методы поиска в строке, поддерживаемые в классе String

Метод Назначение
public bool Contains(string value) Возвращает логическое значение true, если вызывающая строка содержит подстроку value. Если же подстрока value не обнаружена, возвращается логическое значение false
public bool EndsWith(string value) Возвращает логическое значение true, если вызывающая строка оканчивается подстрокой value. В противном случае возвращает логическое значение false
public bool EndsWith(string value, StringComparison comparisonType) Возвращает логическое значение true, если вызывающая строка оканчивается подстрокой value. В противном случае возвращает логическое значение false. Параметр comparisonType определяет конкретный способ поиска
public bool EndsWith(string value, bool ignoreCase, CultureInfo culture) Возвращает логическое значение true, если вызывающая строка оканчивается подстрокой value, иначе возвращает логическое значение false. Если параметр ignoreCase принимает логическое значение true, то при сравнении не учитываются различия между прописным и строчным вариантами букв. В противном случае эти различия учитываются. Поиск осуществляется с использованием информации о культурной среде, обозначаемой параметром culture
public int IndexOf(char value) Возвращает индекс первого вхождения символа value в вызывающей строке. Если искомый символ не обнаружен, то возвращается значение -1
public int IndexOf(string value) Возвращает индекс первого вхождения подстроки value в вызывающей строке. Если искомая подстрока не обнаружена, то возвращается значение -1
public int IndexOf(char value, int startIndex) Возвращает индекс первого вхождения символа value в вызывающей строке. Поиск начинается с элемента, указываемого по индексу startIndex. Метод возвращает значение -1, если искомый символ не обнаружен
public int IndexOf(string value, int startIndex) Возвращает индекс первого вхождения подстроки value в вызывающей строке. Поиск начинается с элемента, указываемого по индексу startIndex. Метод возвращает значение -1, если искомая подстрока не обнаружена
public int IndexOf(char value, int startIndex, int count) Возвращает индекс первого вхождения символа value в вызывающей строке. Поиск начинается с элемента, указываемого по индексу startIndex, и охватывает число элементов, определяемых параметром count. Метод возвращает значение -1, если искомый символ не обнаружен
public int IndexOf(string value, int startIndex, int count) Возвращает индекс первого вхождения подстроки value в вызывающей строке. Поиск начинается с элемента, указываемого по индексу startIndex, и охватывает число элементов, определяемых параметром count. Метод возвращает значение -1, если искомая подстрока не обнаружена
public int IndexOf(string value, StringComparison comparisonType) Возвращает индекс первого вхождения подстроки value в вызывающей строке. Параметр comparisonType определяет конкретный способ выполнения поиска. Метод возвращает значение -1, если искомая подстрока не обнаружена
public int IndexOf(string value, int startIndex, StringComparison comparisonType) Возвращает индекс первого вхождения подстроки value в вызывающей строке. Поиск начинается с элемента, указываемого по индексу startIndex. Параметр comparisonType определяет конкретный способ выполнения поиска. Метод возвращает значение -1, если искомая подстрока не обнаружена
public int IndexOf(string value, int startIndex, int count, StringComparison comparisonType) Возвращает индекс первого вхождения подстроки value в вызывающей строке. Поиск начинается с элемента, указываемого по индексу startIndex, и охватывает число элементов, определяемых параметром count. Параметр comparisonType определяет конкретный способ выполнения поиска. Метод возвращает значение -1, если искомая подстрока не обнаружена
public int LastlndexOf(char value) Возвращает индекс последнего вхождения символа value в вызывающей строке. Если искомый символ не обнаружен, возвращается значение -1
public int IndexOfAny(char[] anyOf) Возвращает индекс первого вхождения любого символа из массива anyOf, обнаруженного в вызывающей строке. Метод возвращает значение -1, если не обнаружено совпадение ни с одним из символов из массива anyOf. Поиск осуществляется порядковым способом
public int IndexOfAny(char[] anyOf, int startIndex) Возвращает индекс первого вхождения любого символа из массива anyOf, обнаруженного в вызывающей строке. Поиск начинается с элемента, указываемого по индексу startIndex Метод возвращает значение -1, если не обнаружено совпадение ни с одним из символов из массива anyOf. Поиск осуществляется порядковым способом
public int IndexOfAny(char[] anyOf, int startIndex, int count) Возвращает индекс первого вхождения любого символа из массива anyOf, обнаруженного в вызывающей строке. Поиск начинается с элемента, указываемого по индексу startIndex, и охватывает число элементов, определяемых параметром count. Метод возвращает значение -1, если не обнаружено совпадение ни с одним из символов из массива anyOf. Поиск осуществляется порядковым способом
public int LastIndexOf(string value) Возвращает индекс последнего вхождения подстроки value в вызывающей строке. Если искомая подстрока не обнаружена, возвращается значение -1
public int LastIndexOf(char value, int startIndex) Возвращает индекс последнего вхождения символа value в части вызывающей строки. Поиск осуществляется в обратном порядке, начиная с элемента, указываемого по индексу startIndex, и заканчивая элементом с нулевым индексом. Метод возвращает значение -1, если искомый символ не обнаружен
public int LastIndexOf(string value, int startIndex) Возвращает индекс последнего вхождения подстроки value в части вызывающей строки. Поиск осуществляется в обратном порядке, начиная с элемента, указываемого по индексу startIndex, и заканчивая элементом с нулевым индексом. Метод возвращает значение -1, если искомая подстрока не обнаружена
public int LastIndexOf(char value, int startIndex, int count) Возвращает индекс последнего вхождения символа value в части вызывающей строки. Поиск осуществляется в обратном порядке, начиная с элемента, указываемого по индексу startIndex, и охватывает число элементов, определяемых параметром count. Метод возвращает значение -1, если искомый символ не обнаружен
public int LastIndexOf(string value, int startIndex, int count) Возвращает индекс последнего вхождения подстроки value в части вызывающей строки. Поиск осуществляется в обратном порядке, начиная с элемента, указываемого по индексу startIndex, и охватывает число элементов, определяемых параметром count. Метод возвращает значение -1, если искомая подстрока не обнаружена
public int LastIndexOf(string value, StringComparison comparisonType) Возвращает индекс последнего вхождения подстроки value в вызывающей строке. Параметр comparisonType определяет конкретный способ выполнения поиска. Метод возвращает значение -1, если искомая подстрока не обнаружена
public int LastIndexOf(string value, int startIndex, StringComparison comparisonType) Возвращает индекс последнего вхождения подстроки value в части вызывающей строки. Поиск осуществляется в обратном порядке, начиная с элемента, указываемого по индексу startIndex, и заканчивая элементом с нулевым индексом. Параметр comparisonType определяет конкретный способ выполнения поиска. Метод возвращает значение -1, если искомая подстрока не обнаружена
public int LastIndexOf(string value, int startIndex, int count, StringComparison comparisonType) Возвращает индекс последнего вхождения подстроки value в части вызывающей строки. Поиск осуществляется в обратном порядке, начиная с элемента, указываемого по индексу startIndex, и охватывает число элементов, определяемых параметром count. Параметр comparisonType определяет конкретный способ выполнения поиска. Метод возвращает значение -1, если искомая подстрока не обнаружена
public int LastIndexOfAny(char[] anyOf) Возвращает индекс последнего вхождения любого символа из массива anyOf, обнаруженного в вызывающей строке. Метод возвращает значение -1, если не обнаружено совпадение ни с одним из символов из массива anyOf. Поиск осуществляется порядковым способом
public int LastIndexOfAny(char[] anyOf, int startIndex) Возвращает индекс последнего вхождения любого символа из массива anyOf, обнаруженного в вызывающей строке. Поиск начинается в обратном порядке с элемента, указываемого по индексу startIndex, и заканчивая элементом с нулевым индексом. Метод возвращает значение -1, если не обнаружено совпадение ни с одним из символов из массива anyOf. Поиск осуществляется порядковым способом
public int LastIndexOfAny(char[] anyOf, int startIndex, int count) Возвращает индекс последнего вхождения любого символа из массива anyOf, обнаруженного в вызывающей строке. Поиск осуществляется в обратном порядке, начиная с элемента, указываемого по индексу startIndex, и охватывает число элементов, определяемых параметром count, число элементов, определяемых параметром count. Метод возвращает значение -1, если не обнаружено совпадение ни с одним из символов из массива anyOf. Поиск осуществляется порядковым способом
public bool StartsWith(string value) Возвращает логическое значение true, если вызывающая строка начинается с подстроки value. В противном случае возвращается логическое значение false
public bool StartsWith(string value, StringComparison comparisonType) Возвращает логическое значение true, если вызывающая строка начинается с подстроки value. В противном случае возвращается логическое значение false. Параметр comparisonType определяет конкретный способ выполнения поиска
public bool StartsWith(string value, bool ignoreCase, CultureInfo culture) Возвращает логическое значение true, если вызывающая строка начинается с подстроки value. В противном случае возвращается логическое значение false. Если параметр ignoreCase принимает логическое значение true, то при сравнении не учитываются различия между прописным и строчным вариантами букв. В противном случае эти различия учитываются. Поиск осуществляется с использованием информации о культурной среде, обозначаемой параметром culture

Разделение и соединение строк

К основным операциям обработки строк относятся разделение и соединение. При разделении строка разбивается на составные части, а при соединении строка состав­ ляется из отдельных частей. Для разделения строк в классе String определен метод Split(), а для соединения — метод Join().

Существует несколько вариантов метода Split(). Ниже приведены две формы этого метода, ставшие наиболее часто используемыми, начиная с версии C# 1.0.

public string[ ] Split(params char[ ] separator)
public string[ ] Split(params char[ ] separator, int count)

В первой форме метода Split() вызывающая строка разделяется на составные части. В итоге возвращается массив, содержащий подстроки, полученные из вызы­ вающей строки. Символы, ограничивающие эти подстроки, передаются в массиве separator. Если массив separator пуст или ссылается на пустую строку, то в каче­ стве разделителя подстрок используется пробел. А во второй форме данного метода возвращается количество подстрок, определяемых параметром count.

Существует несколько форм метода Join(). Ниже приведены две формы, ставшие доступными, начиная с версии 2.0 среды .NET Framework.

public static string Join(string separator, string[] value)
public static string Join(string separator, string[] value,
	int startIndex, int count)

В первой форме метода Join() возвращается строка, состоящая из сцепляемых подстрок, передаваемых в массиве value. Во второй форме также возвращается стро­ ка, состоящая из подстрок, передаваемых в массиве value, но они сцепляются в опре­ деленном количестве count, начиная с элемента массива value[startIndex]. В обе­ их формах каждая последующая строка отделяется от предыдущей разделительной строкой, определяемой параметром separator.

В приведенном ниже примере программы демонстрируется применение методов Split() и Join().

// Разделить и соединить строки.
using System;

class SplitAndJoinDemo {
	static void Main() {
		string str = "Один на суше, другой на море.";
		char[] seps = {' ', '.', ',' };

		// Разделить строку на части.
		string[] parts = str.Split(seps);
		Console.WriteLine("Результат разделения строки: ");
		for(int i=0; i < parts.Length; i++)
			Console.WriteLine(parts[i]);

		// А теперь соединить части строки.
		string whole = String.Join(" | ", parts);
		Console.WriteLine("Результат соединения строки: ");
		Console.WriteLine(whole);
	}
}

Ниже приведен результат выполнения этой программы.

Результат разделения строки:
Один
на
суше

другой
на
море

Результат соединения строки:
Один | на | суше | | другой | на | море

Обратите внимание на пустую строку между словами "суше" и "другой". Дело в том, что в исходной строке после слова "суше" следует запятая и пробел, как в под­ строке "суше, другой". Но запятая и пробел указаны в качестве разделителей. Поэтому при разделении данной строки между двумя разделителями (запятой и пробелом) оказывается пустая строка.

Существует ряд других форм метода Split(), принимающих параметр типа StringSplitOptions. Этот параметр определяет, являются ли пустые строки частью разделяемой в итоге строки. Ниже приведены все эти формы метода Split().

public string[] Split(params char[] separator,StringSplitOptions options)
public string[] Split(string[] separator, StringSplitOptions options)
public string[] Split(params char[] separator, int count,
					StringSplitOptions options)
public string[] Split(string[] separator, int count,
					StringSplitOptions options)

В двух первых формах метода Split() вызывающая строка разделяется на части и возвращается массив, содержащий подстроки, полученные из вызывающей стро­ ки. Символы, разделяющие эти подстроки, передаются в массиве separator. Если массив separator пуст, то в качестве разделителя используется пробел. А в третьей и четвертой формах данного метода возвращается количество строк, ограничиваемое параметром count. Но во всех формах параметр options обозначает конкретный спо­ соб обработки пустых строк, которые образуются в том случае, если два разделителя оказываются рядом. В перечислении StringSplitOptions определяются только два значения: None и RemoveEmptyEntries. Если параметр options принимает значение None, то пустые строки включаются в конечный результат разделения исходной стро­ ки, как показано в предыдущем примере программы. А если параметр options при­ нимает значение RemoveEmptyEntries, то пустые строки исключаются из конечного результата разделения исходной строки.

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

string[] parts = str.Split(seps);

следующим фрагментом кода.

string[] parts = str.Split(seps, StringSplitOptions.RemoveEmptyEntries);

При выполнении данной программы получится следующий результат.

Результат разделения строки:
Один
на
суше
другой
на
море
Результат соединения строки:
Один | на | суше | другой | на | море

Как видите, пустая строка, появлявшаяся ранее из-за того, что после слова "суше" следовали запятая и пробел, теперь исключена.

Разделение является очень важной процедурой обработки строк, поскольку с его помощью нередко получают отдельные лексемы, составляющие исходную строку. Так, в программе ведения базы данных может возникнуть потребность разделить с помо­ щью метода Split() строку запроса "показать все остатки больше 100" на отдель­ ные части, включая подстроки "показать" и "100". В процессе разделения исключа­ ются разделители, поэтому в итоге получается подстрока "показать" (без начальных и конечных пробелов), а не подстрока "показать". Этот принцип демонстрируется в приведенном ниже примере программы, где строки, содержащие такие бинарные математические операции, как 10 + 5, преобразуются в лексемы, а затем эти операции выполняются и выводится конечный результат.

// Преобразовать строки в лексемы.
using System;

class TokenizeDemo {
	static void Main() {
		string[] input = {
						"100 + 19",
						"100 / 3,3",
						"-3 * 9",
						"100 - 87"
		};

		char[] seps = {' '};

		for(int i=0; i < input.Length; i++) {
			// разделить строку на части
			string[] parts = input[i].Split(seps);
			Console.Write("Команда: ");
			for(int j=0; j < parts.Length; j++)
				Console.Write(parts[j] + " ");

			Console.Write(", результат: ");
			double n = Double.Parse(parts[0]);
			double n2 = Double.Parse(parts[2]);

			switch(parts[1]) {
				case "+":
					Console.WriteLine(n + n2);
					break;
				case
					Console.WriteLine(n - n2);
					break;
				case "*":
					Console.WriteLine(n * n2);
					break;
				case "/":
					Console.WriteLine(n / n2);
					break;
			}
		}
	}
}

Вот к какому результату приводит выполнение этой программы.

Команда: 100 + 19 , результат: 119
Команда: 100 / 3,3 , результат: 30,3030303030303
Команда: -3 * 9 , результат: -27
Команда: 100 - 87 , результат: 13

Начиная с версии 4.0, в среде .NET Framework стали доступными следующие до­ полнительные формы метода Join().

public static string Join(string separator, params object[] values)
public static string Join(string separator, IEnumerable<string>[] values)
public static string Join<T>(string separator, IEnumerable<T>[] values)

В первой форме рассматриваемого здесь метода возвращается строка, содержащая строковое представление объектов из массива values. Во второй форме возвращается строка, содержащая результат сцепления коллекции строк, обозначаемой параметром values. И в третьей форме возвращается строка, содержащая результат сцепления строковых представлений объектов из коллекции, обозначаемой параметром values. Во всех трех случаях каждая предыдущая строка отделяется от последующей раздели­ телем, определяемым параметром separator.

Заполнение и обрезка строк

Иногда в строке требуется удалить начальные и конечные пробелы. Такая операция называется обрезкой и нередко требуется в командных процессорах. Например, про­ грамма ведения базы данных способна распознавать команду "print", но пользователь может ввести эту команду с одним иди несколькими начальными и конечными про­ белами. Поэтому перед распознаванием введенной команды необходимо удалить все подобные пробелы. С другой стороны, строку иногда требуется заполнить пробелами, чтобы она имела необходимую минимальную длину. Так, если подготавливается вы­ вод результатов в определенном формате, то каждая выводимая строка должна иметь определенную длину, чтобы сохранить выравнивание строк. Для упрощения подобных операций в C# предусмотрены соответствующие методы.

Для обрезки строк используется одна из приведенных ниже форм метода Trim().

public string Trim()
public string Trim(params char[] trimChars)

В первой форме метода Trim() из вызывающей строки удаляются начальные и конечные пробелы. А во второй форме этого метода удаляются начальные и конечные вхождения в вызывающей строке символов из массива trimChars. В обеих формах возвращается получающаяся в итоге строка.

Строку можно заполнить символами слева или справа. Для заполнения строки сле­ ва служат такие формы метода PadLeft().

public string PadLeft(int totalWidth)
public string PadLeft(int totalWidth, char paddingChar)

В первой форме метода PadLeft() вводятся пробелы с девой стороны вызывающей строки, чтобы ее общая длина стала равной значению параметра totalWidth. А во второй форме данного метода символы, обозначаемые параметром paddingChar, вво­ дятся с левой стороны вызывающей строки, чтобы ее общая длина стала равной зна­ чению параметра totalWidth. В обеих формах возвращается получающаяся в итоге строка. Если значение параметра totalWidth меньше длины вызывающей строки, то возвращается копия неизмененной вызывающей строки.

Для заполнения строки справа служат следующие формы метода PadRight().

public string PadRight(int totalWidth)
public string PadRight(int totalWidth, char paddingChar)

В первой форме метода PadLeft() вводятся пробелы с правой стороны вызываю­ щей строки, чтобы ее общая длина стала равной значению параметра totalWidth. А во второй форме данного метода символы, обозначаемые параметром paddingChar, вводятся с правой стороны вызывающей строки, чтобы ее общая длина стала равной значению параметра totalWidth. B обеих формах возвращается получающаяся в ито­ ге строка. Если значение параметра totalWidth меньше длины вызывающей строки, то возвращается копия неизмененной вызывающей строки.

В приведенном ниже примере программы демонстрируются обрезка и заполнение строк.

// Пример обрезки и заполнения строк.
using System;

class TrimPadDemo {
	static void Main() {
		string str = "тест";
		Console.WriteLine("Исходная строка: " + str);

		// Заполнить строку пробелами слева.
		str = str.PadLeft(10);
		Console.WriteLine (" | " + str + "|");

		// Заполнить строку пробелами справа,
		str = str.PadRight(20);
		Console.WriteLine("|" + str + "|");

		// Обрезать пробелы.
		str = str.Trim();
		Console.WriteLine("|" + str + "|");

		// Заполнить строку символами # слева.
		str = str.PadLeft(10, '#');
		Console.WriteLine("|" + str + "|");

		// Заполнить строку символами # справа.
		str = str.PadRight(20, '#');
		Console.WriteLine("|" + str + "|");

		// Обрезать символы #.
		str = str.Trim('#');
		Console.WriteLine("|" + str + "|");
	}
}

Эта программа дает следующий результат.

Исходная строка: тест
| тест|
| тест |
|тест|
|######тест|
|######тест##########|
|тест|

Вставка, удаление и замена строк

Для вставки одной строки в другую служит приведенный ниже метод Insert():

public string Insert(int startIndex, string value)

где value обозначает строку, вставляемую в вызывающую строку по индексу startIndex. Метод возвращает получившуюся в итоге строку.

Для удаления части строки служит метод Remove(). Ниже приведены две его формы.

public string Remove(int startIndex)
public string Remove(int startIndex, int count)

В первой форме метода Remove() удаление выполняется, начиная с места, указы­ ваемого по индексу startIndex, и продолжается до конца строки. А во второй форме данного метода из строки удаляется количество символов, определяемое параметром count, начиная с места, указываемого по индексу startIndex. В обеих формах воз­ вращается получающаяся в итоге строка.

Для замены части строки служит метод Replace(). Ниже приведены две его формы.

public string Replace(char oldChar, char newChar)
public string Replace(string oldValue, string newValue)

В первой форме метода Replace() все вхождения символа oldChar в вызывающей строке заменяются символом newChar. А во второй форме данного метода все вхож­ дения строки oldValue в вызывающей строке заменяются строкой newValue. В обеих формах возвращается получающаяся в итоге строка.

В приведенном ниже примере демонстрируется применение методов Insert(), Remove() и Replace().

// Пример вставки, замены и удаления строк.
using System;

class InsRepRevDemo {
	static void Main() {
		string str = "Это тест";
		Console.WriteLine("Исходная строка: " + str);

		// Вставить строку.
		str = str.Insert(4, "простой ");
		Console.WriteLine(str);

		// Заменить строку.
		str = str.Replace("простой", "непростой ");
		Console.WriteLine(str);

		// Заменить символы в строке
		str = str.Replace('т', 'X');
		Console.WriteLine(str);

		// Удалить строку.
		str = str.Remove(4, 5);
		Console.WriteLine(str);
	}
}

Ниже приведен результат выполнения этой программы.

Исходная строка: Это тест
Это простой тест
Это непростой тест
ЭХо непросХой ХесХ
ЭХо сХой ХесХ

Смена регистра

В классе String предоставляются два удобных метода, позволяющих сменить ре­ гистр букв в строке, — ToUpper() и ToLower(). Ниже приведены их простейшие формы.

public string ToLower()
public string ToUpper()

Метод ToLower() делает строчными все буквы в вызывающей строке, а метод ToUpper() делает их прописными. В обоих случаях возвращается получающаяся в итоге строка. Имеются также следующие формы этих методов, в которых можно ука­ зывать информацию о культурной среде и способы преобразования символов.

public string ToLower(CultureInfo culture)
public string ToUpper(CultureInfo culture)

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

Кроме того, имеются следующие методы ToUpperInvariant() и ToLowerInvariant().

public string ToUpperlnvariant()
public string ToLowerlnvariant()

Эти методы аналогичны методам ToUpper() и ToLower(), за исключением того, что они изменяют регистр букв в вызывающей строке безотносительно к настройкам культурной среды.

Применение метода Substring()

Для получения части строки служит метод Substring(). Ниже приведены две его формы.

public string Substring(int startIndex)
public string Substring(int startIndex, int length)

В первой форме метода Substring() подстрока извлекается, начиная с места, обозначаемого параметром startIndex, и до конца вызывающей строки. А во вто­ рой форме данного метода извлекается подстрока, состоящая из количества симво­ лов, определяемых параметром length, начиная с места, обозначаемого параметром startIndex. В обеих формах возвращается получающаяся в итоге подстрока.

В приведенном ниже примере программы демонстрируется применение метода Substring().

// Использовать метод Substring().
using System;

class SubstringDemo {
	static void Main() {
		string str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

		Console.WriteLine("Строка str: " + str);

		Console.Write("Подстрока str.Substring(15): ");
		string substr = str.Substring(15);
		Console.WriteLine(substr);

		Console.Write("Подстрока str.Substring(0, 15): ");
		substr = str.Substring(0, 15);
		Console.WriteLine(substr);
	}
}

Эта программа дает следующий результат.

Строка str: ABCDEFGHIJKLMNOPQRSTUVWXYZ
Подстрока str.Substring(15): PQRSTUVWXYZ
Подстрока str.Substring(0, 15): ABCDEFGHIJKLMNO

Методы расширения класса String

Как упоминалось ранее, в классе String реализуется обобщенный интерфейс IEnumerable<T>. Это означает, что, начиная с версии C# 3.0, для объекта класса String можно вызывать методы расширения, определенные в классах Enumerable и Queryable, которые находятся в пространстве имен System.Linq. Эти методы рас­ ширения служат главным образом для поддержки LINQ, хотя некоторые из них могут использоваться в иных целях, в том числе и в определенных видах обработки строк. Подробнее о методах расширения см. в главе 19.

Форматирование

Когда данные встроенных в C# типов, например int или double, требуется ото­ бразить в удобочитаемой форме, приходится формировать их строковое представле­ ние. Несмотря на то что в C# для такого представления данных автоматически предо­ ставляется формат, используемый по умолчанию, имеется также возможность указать выбранный формат вручную. Так, в части I этой книги было показано, что числовые данные можно выводить в формате выбранной денежной единицы. Для форматиро­ вания данных числовых типов в C# предусмотрен целый ряд методов, включая методы Console.WriteLine(), String.Format() и ToString(). Во всех этих методах при­ меняется один и тот же подход к форматированию. Поэтому освоив один из них, вы сможете без особого труда применять и другие.

Общее представление о форматировании

Форматирование осуществляется с помощью двух компонентов: спецификато­ ров формата и поставщиков формата. Конкретная форма строкового представления отдельного значения зависит от спецификатора формата. Следовательно, специфика­ тор формата определяет, в какой именно удобочитаемой форме будут представлены данные. Например, для вывода числового значения в экспоненциальном представле­ нии (т.е. в виде мантиссы и порядка числа) используется спецификатор формата Е.

Как правило, конкретный формат значения зависит от культурных и языковых особенностей локализации программного обеспечения. Например, в Соединенных Штатах Америки денежные суммы указываются в долларах, а в странах ЕС — в евро. Для учета культурных и языковых отличий в C# предусмотрены поставщики фор­ мата. В частности, поставщик формата определяет порядок интерпретации специ­ фикатора формата. Поставщик формата создается путем реализации интерфейса IFormatProvider, в котором определяется метод GetFormat(). Для всех встроенных числовых типов и многих других типов данных в среде .NET Framework предопределе­ ны соответствующие поставщики формата. Вообще говоря, данные можно отформа­ тировать, не указывая конкретный поставщик формата, поэтому поставщики формата не рассматриваются далее в этой книге.

Для того чтобы отформатировать данные, достаточно включить спецификатор формата в метод, поддерживающий форматирование. О применении спецификато­ ров формата речь уже шла в главе 3, тем не менее к этому вопросу стоит вернуться вновь. Применение спецификаторов формата рассматривается далее на примере ме­ тода Console.WriteLine(), хотя аналогичный подход применим и к другим мето­ дам, поддерживающим форматирование.

Для форматирования выводимых данных служит следующая форма метода WriteLine().

WriteLine("форматирующая строка", arg0, arg1, ... , argN);

В этой форме аргументы метода WriteLine() разделяются запятой, а не знаком +. А форматирующая строка состоит из двух следующих элементов: обычных печатае­ мых символов, отображаемых в исходном виде, а также команд форматирования.

Ниже приведена общая форма команд форматирования:

{argnum, width: fmt}

где argnum — это номер отображаемого аргумента, начиная с нуля; width — мини­ мальная ширина поля, a fmt — спецификатор формата. Параметры width и fmt не являются обязательными. Поэтому в своей простейшей форме команда форматиро­ вания просто указывает конкретные аргументы для отображения. Например, команда {0} указывает аргумент arg0, команда {1} — аргумент arg1 и т.д.

Если во время выполнения программы в форматирующей строке встречается команда форматирования, то вместо нее подставляется и затем отображается соот­ ветствующий аргумент, определяемый параметром argnum. Следовательно, от поло­ жения спецификатора формата в форматирующей строке зависит, где именно будут отображаться соответствующие данные. А номер аргумента определяет конкретный форматируемый аргумент.

Если в команде форматирования указывается параметр fmt, то данные отображают­ ся в указываемом формате. В противном случае используется формат, выбираемый по умолчанию. Если же в команде форматирования указывается параметр width, то вы­ водимые данные дополняются пробелами для достижения минимально необходимой ширины поля. При положительном значении параметра width выводимые данные вы­ равниваются по правому краю, а при отрицательном значении — по левому краю.

Оставшаяся часть данной главы посвящена вопросам форматирования и отдель­ ным спецификаторам формата.

Спецификаторы формата числовых данных

Для числовых данных определено несколько спецификаторов формата, сведенных в табл. 22.4. Каждый спецификатор формата может включать в себя дополнительный, но необязательный спецификатор точности. Так, если числовое значение требуется указать в формате с фиксированной точкой и двумя десятичными разрядами в дроб­ ной части, то для этой цели служит спецификатор F2.

Таблица 22.4. Спецификаторы формата числовых данных

Спецификатор Формат Назначение спецификатора точности
С Денежная единица Задает количество десятичных разрядов
с То же, что и С
D Целочисленный (используется только с целыми числами) Задает минимальное количество цифр. При необходимости результат дополняется начальными нулями
d То же, что и D
Е Экспоненциальное представление чисел (в обозначении используется прописная буква Е) Задает количество десятичных разрядов. По умолчанию используется шесть разрядов
е Экспоненциальное представление чисел (в обозначении используется строчная буква е) Задает количество десятичных разрядов. По умолчанию используется шесть разрядов
F Представление чисел с фиксированной точкой Задает количество десятичных разрядов
f То же, что и F
G Используется более короткий из двух форматов: Е или F См. спецификаторы Е и F
g Используется более короткий из двух форматов: е или f См. спецификаторы е и f
N Представление чисел с фиксированной точкой (и запятой в качестве разделителя групп разрядов) Задает количество десятичных разрядов
n То же, что и N
Р Проценты Задает количество десятичных разрядов
Р То же, что и Р
R или r Числовое значение, которое преобразуется с помощью метода Parse() в эквивалентную внутреннюю форму. (Это так называемый "круговой" формат) Не используется
X Шестнадцатеричный (в обозначении используются прописные буквы A-F) Задает минимальное количество цифр. При необходимости результат дополняется начальными нулями
X Шестнадцатеричный (в обозначении используются строчные буквы A-F) Задает минимальное количество цифр. При необходимости результат дополняется начальными нулями

Как пояснялось выше, конкретное действие спецификаторов формата зависит от текущих настроек параметров культурной среды. Например, спецификатор денежной единицы С автоматически отображает числовое значение в формате денежной едини­ цы, выбранном для локализации программного обеспечения в конкретной культурной среде. Для большинства пользователей используемая по умолчанию информация о культурной среде соответствует их региональным стандартам и языковым особенно­ стям. Поэтому один и тот же спецификатор формата может использоваться без учета культурного контекста, в котором выполняется программа.

В приведенной ниже программе демонстрируется применение нескольких специ­ фикаторов формата числовых данных.

// Продемонстрировать применение различных
// спецификаторов формата числовых данных.
using System;

class FormatDemo {
	static void Main() {
		double v = 17688.65849;
		double v2 = 0.15;
		int x = 21;

		Console.WriteLine("{0:F2}", v);

		Console.WriteLine("{0:N5}", v);

		Console.WriteLine("{0:e}", v);

		Console.WriteLine("{0:r}", v);

		Console.WriteLine("{0:p}", v2);

		Console.WriteLine("{0:X}", x);

		Console.WriteLine("{0:D12}", x);

		Console.WriteLine("{0:C}", 189.99);
	}
}

Эта программа дает следующий результат.

17688.66
17.688.65849
1.768866е+004
17688.65849
15.00 %
15
000000000021
$189.99

Обратите внимание на действие спецификатора точности в нескольких форматах.

Представление о номерах аргументов

Следует иметь в виду, что аргумент, связанный со спецификатором формата, опре­ деляется номером аргумента, а не его позицией в списке аргументов. Это означает, что один и тот же аргумент может указываться неоднократно в одном вызове метода WriteLine(). Эта также означает, что аргументы могут отображаться в той последо­ вательности, в какой они указываются в списке аргументов. В качестве примера рас­ смотрим следующую программу.

using System;

class FormatDemo2 {
	static void Main() {
		// Форматировать один и тот же аргумент тремя разными способами.
		Console.WriteLine("{0:F2} {0:F3} {0:е}", 10.12345);

		// Отобразить аргументы не по порядку.
		Console.WriteLine("{2:d} {0:d} {l:d}", 1, 2, 3);
	}
}

Ниже приведен результат выполнения этой программы.

10.12 10.123 1.012345е+001
3 1 2

В первом операторе вызова метода WriteLine() один и тот же аргумент 10.12345 форматируется тремя разными способами. Это вполне допустимо, поскольку каждый спецификатор формата в этом вызове обозначает первый и единственный аргумент. А во втором вызове метода WriteLine() три аргумента отображаются не по порядку. Не следует забывать, что каких-то особых правил, предписывающих обозначать аргу­ менты в спецификаторах формата в определенной последовательности, не существует. Любой спецификатор формата может обозначать какой угодно аргумент.

Применение методов String.Format() и ToString() для форматирования данных

Несмотря на все удобства встраивания команд форматирования выводимых дан­ ных в вызовы метода WriteLine(), иногда все же требуется сформировать строку, со­ держащую отформатированные данные, но не отображать ее сразу. Это дает возмож­ ность отформатировать данные заранее, чтобы вывести их в дальнейшем на выбранное устройство. Такая возможность особенно полезна для организации работы в среде с графическим пользовательским интерфейсом, подобной Windows, где ввод-вывод на консоль применяется редко, а также для подготовки вывода на веб-страницу.

Вообще говоря, отформатированное строковое представление отдельного зна­ чения может быть получено двумя способами. Один из них состоит в применении метода String.Format(), а другой — в передаче спецификатора формата методу ToString(), относящемуся к одному из встроенных в С# числовых типов данных. Оба способа рассматриваются далее по порядку.

Применение метода String.Format() для форматирования значений

Для получения отформатированного значения достаточно вызвать метод Format(), определенный в классе String, в соответствующей его форме. Все формы этого метода перечислены в табл. 22.5. Метод Format() аналогичен методу WriteLine(), за ис­ ключением того, что он возвращает отформатированную строку, а не выводит ее на консоль.

Таблица 22.5. Формы метода Format()

Метод Описание
public static string Format(string format, object arg0) Форматирует объект arg0 в соответствии с первой командой форматирования, которая содержится в строке format. Возвращает копию строки format, в которой команда форматирования заменена отформатированными данными
public static string Format(string format, object arg0, object arg1) Форматирует объект arg0 в соответствии с первой командой форматирования, содержащейся в строке format, а объект arg1 — в соответствии со второй командой. Возвращает копию строки format, в которой команды форматирования заменены отформатированными данными
public static string Format(string format, object arg0, object arg1, object arg2) Форматирует объекты arg0, arg1 и arg2 по соответствующим командам форматирования, содержащимся в строке format. Возвращает копию строки format, в которой команды форматирования заменены отформатированными данными
public static string Format(string format, params object[] args) Форматирует значения, передаваемые в массиве args, в соответствии с командами форматирования, содержащимися в строке format. Возвращает копию строки format, в которой команды форматирования заменены отформатированными данными
public static string Format(IFormatProvider provider, string format, params object[] args) Форматирует значения, передаваемые в массиве args, в соответствии с командами форматирования, содержащимися в строке format, используя поставщик формата provider. Возвращает копию строки format, в которой команды форматирования заменены отформатированными данными

Ниже приведен вариант предыдущего примера программы форматирования, из­ мененный с целью продемонстрировать применение метода String.Format(). Этот вариант дает такой же результат, как и предыдущий.

// Использовать метод String.Format() для форматирования значений.
using System;

class FormatDemo {
	static void Main() {
		double v = 17688.65849;
		double v2 = 0.15;
		int x = 21;

		string str = String.Format("{0:F2}", v);
		Console.WriteLine(str);

		str = String.Format("{0:N5}", v);
		Console.WriteLine(str);

		str = String.Format("{0:e}", v);
		Console.WriteLine(str);

		str = String.Format("{0:r}", v);
		Console.WriteLine(str);

		str = String.Format("{0:p}", v2);
		Console.WriteLine(str);

		str = String.Format("{0:X}", x);
		Console.WriteLine(str);

		str = String.Format("{0:D12}", x);
		Console.WriteLine(str);

		str = String.Format("{0:C}", 189.99);
		Console.WriteLine(str);
	}
}

Аналогично методу WriteLine(), метод String.Format() позволяет встраивать в свой вызов обычный текст вместе со спецификаторами формата, причем в вызове данного метода может быть указано несколько спецификаторов формата и значений. В качестве примера рассмотрим еще одну программу, отображающую текущую сум­ му и произведение чисел от 1 до 10.

// Еще один пример применения метода Format().
using System;

class FormatDemo2 {
	static void Main() {
		int i;
		int sum = 0;
		int prod = 1;
		string str;

		/* Отобразить текущую сумму и произведение чисел
			от 1 до 10. */
		for(i=1; i <= 10; i++) {
			sum += i;
			prod *= i;
			str = String.Format("Сумма:{0,3:D} Произведение:{1,8:D}",
								sum, prod);
			Console.WriteLine(str);
		}
	}
}

Ниже приведен результат выполнения этой программы.

Сумма:  1 Произведение:       1
Сумма:  3 Произведение:       2
Сумма:  6 Произведение:       6
Сумма: 10 Произведение:      24
Сумма: 15 Произведение:     120
Сумма: 21 Произведение:     720
Сумма: 28 Произведение:    5040
Сумма: 36 Произведение:   40320
Сумма: 45 Произведение:  362880
Сумма: 55 Произведение: 3628800

Обратите особое внимание в данной программе на следующий оператор.

str = String.Format("Сумма:{0,3:D} Произведение:{1,8:D}", sum, prod);

В этом операторе содержится вызов метода Format() с двумя спецификатора­ ми формата: одним — для суммы (в переменной sum), а другим — для произведения (в переменной prod). Обратите также внимание на то, что номера аргументов указы­ ваются таким же образом, как и в вызове метода WriteLine(), и что в вызов метода Format() включается обычный текст, как, например, строка "Сумма: ". Этот текст передается данному методу и становится частью выводимой строки.

Применение метода ToString() для форматирования данных

Для получения отформатированного строкового представления отдельного значе­ ния любого числового типа, которому соответствует встроенная структура, например Int32 или Double, можно воспользоваться методом ToString(). Этой цели служит приведенная ниже форма метода ToString().

public string ToString("форматирующая строка")

В этой форме метод ToString() возвращает строковое представление вызываю­ щего объекта в том формате, который определяет спецификатор "форматирующая строка", передаваемый данному методу. Например, в следующей строке кода фор­ мируется строковое представление значения 188.99 в формате денежной единицы с помощью спецификатора формата С.

string str = 189.99.ToString("С");

Обратите внимание на то, что спецификатор формата передается методу ToString() непосредственно. В отличие от встроенных команд форматирования, ис­ пользуемых в вызовах методов WriteLine() и Format(), где для этой цели допол­ нительно указываются номер аргумента и ширина поля, в вызове метода ToString()

достаточно указать только спецификатор формата. Ниже приведен вариант примера предыдущей программы форматирования, изме­ ненный с целью продемонстрировать применение метода ToString() для получения отформатированных строк. Этот вариант дает такой же результат, как и предыдущий.

// Использовать метод ToString() для форматирования значений.
using System;

class ToStringDemo {
	static void Main() {
		double v = 17688.65849;
		double v2 = 0.15;
		int x = 21;

		string str = v.ToString("F2");
		Console.WriteLine(str);

		str = v.ToString("N5");
		Console.WriteLine(str);

		str = v.ToString("e");
		Console.WriteLine (str);

		str = v.ToString("r");
		Console.WriteLine(str);

		str = v2.ToString("p");
		Console.WriteLine(str);

		str = x.ToString("X");
		Console.WriteLine(str);

		str = x.ToString("D12");
		Console.WriteLine(str);

		str = 189.99.ToString("C");
		Console.WriteLine(str);
	}
}

Определение пользовательского формата числовых данных

Несмотря на всю полезность предопределенных спецификаторов формата число­ вых данных, в C# предоставляется также возможность определить пользовательский, т.е. свой собственный, формат, используя средство, называемое форматом изображе­ ния. Своим происхождением термин формат изображения обязан тому обстоятельству, что специальный формат пользователь определяет, задавая пример внешнего вида (т.е. изображение) выводимых данных. Такой подход вкратце упоминался в части I этой книги, а здесь он рассматривается более подробно.

Символы-заполнители специального формата числовых данных

Когда пользователь определяет специальный формат, он задает этот формат в виде примера (иди изображения) того, как должны выглядеть выводимые данные. Для этой цеди используются символы, перечисленные в табл. 22.6. Они служат в качестве запол­ нителей и рассматриваются далее по очереди.

Символ точки обозначает местоположение десятичной точки.

Символ-заполнитель # обозначает цифровую позицию, иди разряд числа. Этот символ может указываться слева иди справа от десятичной точки либо отдельно. Так, если справа от десятичной точки указывается несколько символов #, то они обо­ значают количество отображаемых десятичных цифр в дробной части числа. При необходимости форматируемое числовое значение округляется. Когда же символы # указываются слева от десятичной точки, то они обозначают количество отображаемых десятичных цифр в целой части числа. При необходимости форматируемое число­ вое значение дополняется начальными нулями. Если целая часть числового значения состоит из большего количества цифр, чем количество указываемых символов #, то она отображается полностью, но в любом случае целая часть числового значения не усекается. В отсутствие десятичной точки наличие символа # обусловливает округле­ ние соответствующего целого значения. А нулевое значение, которое не существенно, например конечный нуль, не отображается. Правда, это обстоятельство несколько усложняет дело, поскольку при указании такого формата, как #.##, вообще ничего не отображается, если форматируемое числовое значение равно нулю. Для вывода нуле­ вого значения служит рассматриваемый далее символ-заполнитель 0.

Таблица 22.6. Символы-заполнители специального формата числовых данных

Символ-заполнитель Назначение
# Цифра
. Десятичная точка
, Разделитель групп разрядов
% Процент
0 Используется для дополнения начальными и конечными нулями
; Выделяет разделы, описывающие формат для положительных, отрицательных и нулевых значений
Е0 Е+0 Е-0 е0 е+0 е-0 Экспоненциальное представление чисел

Символ-заполнитель 0 обусловливает дополнение форматируемого числового зна­ чения начальными или конечными нулями, чтобы обеспечить минимально необхо­ димое количество цифр в строковом представлении данного значения. Этот символ может указываться как слева, как и справа от десятичной точки. Например, следующая строка кода:

Console.WriteLine("{0:00##.#00}", 21.3);

выводит такой результат.

0021.300

Значения, состоящие из большего количества цифр, будут полностью отображать­ ся слева от десятичной точки, а округленные — справа.

При отображении больших числовых значений отдельные группы цифр могут от­ деляться друг от друга запятыми, для чего достаточно вставить запятую в шаблон, со­ стоящий из символов #. Например, следующая строка кода:

Console.WriteLine("{0:#,###.#}", 3421.3);

выводит такой результат.

3,421.3.

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

Console.WriteLine("{0:#,###.#}", 8763421.3);

дает такой результат.

8,763,421.3.

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

Console.WriteLine("Значение в тысячах: {0:#,###,.#}", 8763421.3);

дает такой результат.

Значение в тысячах: 8,763.4

Как показывает приведенный выше результат, числовое значение выводится мас­ штабированным в тысячах.

Помимо символов-заполнителей, пользовательский спецификатор формата может содержать любые другие символы, которые появляются в отформатированной строке без изменения на тех местах, где они указаны в спецификаторе формата. Например, при выполнении следующего фрагмента кода:

Console.WriteLine("КПД топлива: (0:##.# миль на галлон }", 21.3);

выводится такой результат.

КПД топлива: 21.3 миль на галлон

При необходимости в форматируемой строке можно также указывать такие управ­ ляющие последовательности, как \t или \n.

Символы-заполнители Е и е обусловливают отображение числовых значений в экс­ поненциальном представлении. В этом случае после символа Е или е должен быть указан хотя бы один нуль, хотя их может быть и больше. Нули обозначают количество отображаемых десятичных цифр. Дробная часть числового значения округляется в со­ ответствии с заданным форматом отображения. Если указывается символ Е, то он ото­ бражается прописной буквой "Е". А если указывается символ е, то он отображается строчной буквой "е". Для того чтобы знак порядка отображался всегда, используются формы Е+ или е+. А для отображения знака порядка только при выводе отрицатель­ ных значений служат формы Е, е, Е- или е-.

Знак ; служит разделителем в различных форматах вывода положительных, от­ рицательных и нулевых значений. Ниже приведена общая форма пользовательского спецификатора формата, в котором используется знак ;. положительный_формат; отрицательный_формат; нулевой_формат Рассмотрим следующий пример.

Console.WriteLine("{0:#.##; (#.##);0.00}", num);

Если значение переменной num положительно, то оно отображается с двумя раз­ рядами после десятичной точки. Если же значение переменной num отрицательно, то оно также отображается с двумя разрядами после десятичной точки, но в круглых скобках. А если значение переменной num равно нулю, то оно отображается в виде строки 0.00. Когда используются разделители, указывать все части приведенной выше формы пользовательского спецификатора формата совсем не обязательно. Так, если требуется вывести только положительные или отрицательные значения, нулевой_ формат можно опустить. (В данном случае нуль форматируется как положительное значение.) С другой стороны, можно опустить отрицательный_формат. И в этом слу­ чае положительный_формат и нулевой_формат должны разделяться точкой с запя­ той. А в итоге положительный_формат будет использоваться для форматирования не только положительных, но и отрицательных значений.

В приведенном ниже примере программы демонстрируется лишь несколько спе­ циальных форматов, которые могут быть определены пользователем.

// Пример применения специальных форматов.
using System;

class PictureFormatDemo {
	static void Main() {
		double num = 64354.2345;
		Console.WriteLine("Формат по умолчанию: " + num);

		// Отобразить числовое значение с 2 разрядами после десятичной точки.
		Console.WriteLine("Значение с 2 десятичными разрядами: " +
						"(0:#.##)", num);

		// Отобразить числовое значение с 2 разрядами после
		// десятичной точки и запятыми перед ней.
		Console.WriteLine("Добавить запятые: (0:#,###.##}", num);

		// Отобразить числовое значение в экспоненциальном представлении.
		Console.WriteLine("Использовать экспоненциальное представление: " +
						"{0:#.###е+00}", num);

		// Отобразить числовое значение, масштабированное в тысячах.
		Console.WriteLine("Значение в тысячах: " + "(0:#0,)", num);

		/* Отобразить по-разному положительные,
			отрицательные и нулевые значения. */
		Console.WriteLine("Отобразить по-разному положительные," +
						"отрицательные и нулевые значения.");
		Console.WriteLine("{0:#.#;(#.##);0.00}", num);
		num = -num;
		Console.WriteLine("{0:#.##;(#.##);0.00}", num);
		num = 0.0;
		Console.WriteLine("{0:#.##;(#.##);0.00}", num);

		// Отобразить числовое значение в процентах.
		num = 0.17;
		Console.WriteLine("Отобразить в процентах: {0:#%}", num);
	}
}

Ниже приведен результат выполнения этой программы.

Формат по умолчанию: 64354.2345
Значение с 2 десятичными разрядами: 64354.23
Добавить запятые: 64,354.23
Использовать экспоненциальное представление: 6.435е+04
Значение в тысячах: 64
Отобразить по-разному положительные, отрицательные и нулевые значения.
64354.2
 (64354.23)
0.00
Отобразить в процентах: 17%

Форматирование даты и времени

Помимо числовых значений, форматированию нередко подлежит и другой тип данных: DateTime. Это структура, представляющая дату и время. Значения даты и времени могут отображаться самыми разными способами. Ниже приведены лишь не­ которые примеры их отображения.

06/05/2005
Friday, January 1, 2010
12:59:00
12:59:00 PM

Кроме того, дата и время могут быть по-разному представлены в отдельных стра­ нах. Для этой цели в среде .NET Framework предусмотрена обширная подсистема форматирования значений даты и времени.

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

Таблица 22.7. Спецификаторы формата даты и времени

Спецификатор Формат
D Дата в длинной форме
d Дата в краткой форме
F Дата и время в длинной форме
f Дата и время в краткой форме
G Дата — в краткой форме, время — в длинной
gg Дата и время — в краткой форме
м Месяц и день
m То же, что и M
O Формат даты и времени, включая часовой пояс. Строка, составленная в формате O, может быть преобразована обратно в эквивалентную форму вывода даты и времени. Это так называемый “круговой” формат
o То же, что и O
R Дата и время в стандартной форме по Гринвичу
r То же, что и R
S Сортируемый формат представления даты и времени
Т Время в длинной форме
t Время в краткой форме
U Длинная форма универсального представления даты и времени; время отображается как универсальное синхронизированное время (UTC)
u Краткая форма универсального представления даты и времени
Y Месяц и год
y То же, что и Y

В приведенном ниже примере программы демонстрируется применение специфи­ каторов формата даты и времени.

// Отформатировать дату и время, используя стандартные форматы.
using System;

class TimeAndDateFormatDemo {
	static void Main() {
		DateTime dt = DateTime.Now; // получить текущее время

		Console.WriteLine("Формат d: {0:d}", dt);
		Console.WriteLine("Формат D: {0:D}", dt);

		Console.WriteLine("Формат t: {0:t}", dt);
		Console.WriteLine("Формат T: {0:T}", dt);

		Console.WriteLine("Формат f: {0:f}", dt);
		Console.WriteLine("Формат F: {0:F}", dt);

		Console.WriteLine("Формат g: {0:g}", dt);
		Console.WriteLine("Формат G: {0:G}", dt);

		Console.WriteLine("Формат m: {0:m}", dt);
		Console.WriteLine("Формат M: {0:M}", dt);

		Console.WriteLine("Формат о: (0:o)", dt);
		Console.WriteLine("Формат O: (0:O}", dt);

		Console.WriteLine("Формат r: {0:r}", dt);
		Console.WriteLine("Формат R: {0:R}", dt);

		Console.WriteLine("Формат s: {0:s}", dt);

		Console.WriteLine("Формат u: {0:u}", dt);
		Console.WriteLine("Формат U: {0:U}", dt);

		Console.WriteLine("Формат у: {0:у}", dt);
		Console.WriteLine("Y format: {0:Y}", dt);
	}
}

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

Формат d: 2/11/2010
Формат D: Thursday, February 11, 2010
Формат t: 11:21 AM
Формат T: 11:21:23 AM
Формат f: Thursday, February 11, 2010 11:21 AM
Формат F: Thursday, February 11, 2010 11:21:23 AM
Формат g: 2/11/2010 11:21 AM
Формат G: 2/11/2010 11:21:23 AM
Формат m: February 11
Формат M: February 11
Формат о: 2010-02-11T11:21:23.3768153-06:00
Формат О: 2010-02-11T11:21:23.3768153-06:00
Формат r: Thu, 11 Feb 2010 11:21:23 GMT
Формат R: Thu, 11 Feb 2010 11:21:23 GMT
Формат s: 2010-02-11T11:21:23
Формат u: 2010-02-11 11:21:23Z
Формат U: Thursday, February 11, 2010 5:21:23 PM
Формат у: February, 2010
Формат Y: February, 2010

В следующем примере программы воспроизводятся очень простые часы. Время об­ новляется каждую секунду, и каждый час компьютер издает звонок. Для получения от­ форматированного строкового представления времени перед его выводом в этой про­ грамме используется метод ToString() из структуры DateTime. Через каждый час символ звукового предупреждающего сигнала присоединяется к отформатированной строке, представляющей время, в результате чего звенит звонок.

// Пример простых часов.
using System;

class SimpleClock {
	static void Main() {
		string t;
		int seconds;

		DateTime dt = DateTime.Now;
		seconds = dt.Second;

		for(;;) {
			dt = DateTime.Now;
			// обновлять время через каждую секунду
			if (seconds != dt.Second) {
				seconds = dt.Second;

				t = dt.ToString("T");

				if(dt.Minute==0 && dt.Second==0)
					t = t + "\a"; // производить звонок через каждый час

				Console.WriteLine(t);
			}
		}
	}
}

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

Несмотря на то что стандартные спецификаторы формата даты и времени пред­ усмотрены практически на все случаи жизни, пользователь может определить свои собственные специальные форматы. Процесс определения пользовательских форма­ тов даты и времени мало чем отличается от описанного выше для числовых типов зна­ чений. По существу, пользователь создает пример (т.е. изображение) того, как долж­ ны выглядеть выводимые данные даты и времени. Для определения пользовательского формата даты и времени служат символы-заполнители, перечисленные в табл. 22.8.

Таблица 22.8. Символы-заполнители специального формата даты и времени

Символ-заполнитель Назначение
d День месяца в виде числа в пределах от 1 до 31
dd День месяца в виде числа в пределах от 1 до 31. Числовые значения в пределах от 1 до 9 дополняются начальным нулем
ddd Сокращенное название дня недели
dddd Полное название дня недели
f, ff, fff, ffff, fffff, ffffff, fffffff Дробная часть числового значения, обозначающего секунды. Количество десятичных разрядов определяется числом заданных символов f
g Эра
h Часы в виде числа в пределах от 1 до 12
hh Часы в виде числа в пределах от 1 до 12. Числовые значения в пределах от 1 до 9 дополняются начальным нулем
H Часы в виде числа в пределах от 0 до 23
HH Часы в виде числа в пределах от 0 до 23. Числовые значения в пределах от 1 до 9 дополняются начальным нулем
К Часовой пояс, указываемый в часах. Для автоматической коррекции местного времени и универсального синхронизированного времени (UTC) используется значение свойства DateTime.Kind. (Этот спецификатор формата рекомендуется теперь вместо спецификаторов с символамизаполнителями Z.)
m Минуты
mm Минуты. Числовые значения в пределах от 1 до 9 дополняются начальным нулем
M Месяц в виде числа в пределах от 1 до 12
MM Месяц в виде числа в пределах от 1 до 12. Числовые значе­
ния в пределах от 1 до 9 дополняются начальным нулем
МММ Сокращенное название месяца
MMMM Полное название месяца
s Секунды
ss Секунды. Числовые значения в пределах от 1 до 9 дополняются начальным нулем
t Символ "А” или “Р”, обозначающий время А.М. (до полудня) или P.M. (после полудня) соответственно
tt A.M. или P.M.
У Год в виде двух цифр, если недостаточно одной
УУ Год в виде двух цифр. Числовые значения в пределах от 1 до 9 дополняются начальным нулем
УУУ Год в виде трех цифр
УУУУ Год в виде четырех цифр
УУУУУ Год в виде пяти цифр
z Смещение часового пояса в часах
zz Смещение часового пояса в часах. Числовые значения в пределах от 1 до 9 дополняются начальным нулем
zzz Смещение часового пояса в часах и минутах
: Разделитель для составляющих значения времени
/ Разделитель для составляющих значения даты
%fmt Стандартный формат, соответствующий спецификатору формата fmt

Глядя на табл. 22.8, можно заметить, что символы-заполнители d, f, g, m, M, s и t выполняют ту же функцию, что и аналогичные символы-заполнители из табл. 22.7. Вообще говоря, если один из этих символов указывается отдельно, то он интерпре­ тируется как спецификатор формата. В противном случае он считается символом- заполнителем. Поэтому если требуется указать несколько таких символов отдельно, но интерпретировать их как символы-заполнители, то перед каждым из них следует поставить знак %.

В приведенном ниже примере программы демонстрируется применение несколь­ ких форматов даты и времени.

// Отформатировать дату и время, используя специальные форматы.
using System;

class CustomTimeAndDateFormatsDemo {
	static void Main() {
		DateTime dt = DateTime.Now;

		Console.WriteLine("Время: {0:hh:mm tt}", dt);
		Console.WriteLine("Время в 24-часовом формате: {0:HH:mm}", dt);
		Console.WriteLine("Дата: {0:ddd МММ dd, yyyy}", dt);

		Console.WriteLine("Эра: {0:gg}", dt);

		Console.WriteLine("Время в секундах: " +
						"{0:HH:mm:ss tt}", dt);

		Console.WriteLine("День месяца в формате m: {0:m}", dt);
		Console.WriteLine("Минуты в формате m: {0:%m}", dt);
	}
}

Вот к какому результату приводит выполнение этой программы (опять же все за­ висит от конкретных настроек языковых и региональных параметров локализации ба­ зового программного обеспечения).

Время: 11:19 AM
Время 24-часовом формате: 11:19
Дата: Thu Feb 11, 2010
Эра: A.D.
Время в секундах: 11:19:40 AM
День месяца в формате m: February 11
Минуты в формате т: 19

Форматирование промежутков времени

Начиная с версии 4.0, в среде .NET Framework появилась возможность формати­ ровать объекты типа TimeSpan — структуры, представляющей промежуток времени. Объект типа TimeSpan может быть получен самыми разными способами, в том числе и в результате вычитания одного объекта типа DateTime из другого. И хотя формати­ ровать объекты типа TimeSpan приходится нечасто, о такой возможности все же стоит упомянуть вкратце.

По умолчанию в структуре TimeSpan поддерживаются три стандартных специ­ фикатора формата даты и времени: с, g и G. Они обозначают инвариантную форму промежутка времени, короткую и длинную форму с учетом культурной среды соот­ ветственно (последняя форма всегда включает в себя дни). Кроме того, в структуре TimeSpan поддерживаются специальные спецификаторы формата даты и времени, приведенные в табл. 22.9. Вообще говоря, если один из этих спецификаторов исполь­ зуется в отдельности, его нужно предварить символом %.

Таблица 22.9. Символы-заполнители специального формата промежутка времени

Символ-заполнитель Назначение
d, dd, ddd, dddd, ddddd, dddddd, ddddddd Целые дни. Если указано несколько символов-заполнителей d, то отображается, по крайней мере, указанное количество цифр с начальными нулями, если требуется
h, hh Часы (не считая тех, что составляют часть целого дня). Если указано hh, то отображаются две цифры с начальными нулями, если требуется
m, mm Минуты (не считая тех, что составляют часть целого часа). Если указано mm, то отображаются две цифры с начальными нулями, если требуется
s, ss Секунды (не считая тех, что составляют часть целой минуты). Если указано ss, то отображаются две цифры с начальными нулями, если требуется
f, ff, fff, ffff, fffff, ffffff, fffffff Дробные доли секунды. Количество символов-заполнителей f обозначает точность представления, а остальные цифры отбрасываются
F, FF, FFF, FFFF, FFFFF, FFFFFF, FFFFFFF Дробные доли секунды. Количество символов-заполнителей F обозначает точность представления, а остальные цифры отбрасываются и конечные нули не отображаются

В приведенной ниже программе демонстрируется форматирование объектов типа TimeSpan на примере отображения времени, которое приблизительно требуется для вывода на экран 1000 целых значений в цикле for.

// Отформатировать объект типа TimeSpan.
using System;

class TimeSpanDemo {
	static void Main() {
		DateTime start = DateTime.Now;

		// Вывести числа от 1 до 1000.
		for(int i = 1; i <= 1000; i++) {
			Console.Write(i + " ");
			if((i % 10) == 0) Console.WriteLine();
		}

		Console.WriteLine();

		DateTime end = DateTime.Now;

		TimeSpan span = end - start;

		Console.WriteLine("Время выполнения: {0:c}", span);
		Console.WriteLine("Время выполнения: {0:g}", span);
		Console.WriteLine("Время выполнения: {0:G}", span);
		Console.WriteLine("Время выполнения: 0.{0:fff} секунды", span);
	}
}

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

981 982 983 984 985 986 987 988 989 990
991 992 993 994 995 996 997 998 999 1000
Время выполнения: 00:00:00.0140000
Время выполнения: 0:00:00.014
Время выполнения: 0:00:00:00.0140000
Время выполнения: 0.014 секунды

Форматирование перечислений

В C# допускается также форматировать значения, определяемые в перечислении. Вообще говоря, значения из перечисления могут отображаться как по имени, так и по значению. Спецификаторы формата перечислений сведены в табл. 22.10. Обратите особое внимание на форматы G и F. Перед перечислениями, которые должны пред­ ставлять битовые поля, следует указывать атрибут Flags. Как правило, в битовых по­ лях хранятся значения, обозначающие отдельные двоичные разряды и упорядоченные по степени числа 2. При наличии атрибута Flags имена всех битовых составляющих форматируемого значения, если, конечно, это действительное значение, отображают­ ся с помощью спецификатора G. А с помощью спецификатора F отображаются имена всех битовых составляющих форматируемого значения, если оно составляется путем логического сложения по ИЛИ двух иди более полей, определяемых в перечислении.

Таблица 22.10. Спецификаторы формата перечислений

Спецификатор Назначение
D Отображает значение в виде десятичного целого числа
d То же, что и D
F Отображает имя значения. Если это значение можно создать путем логического сложения по ИЛИ двух или более полей, определенных в перечислении, то данный спецификатор отображает имена всех битовых составляющих заданного значения, причем независимо от того, задан атрибут Flags или нет
f То же, что и F
G Отображает имя значения. Если перед форматируемым перечислением указывается атрибут Flags, то данный спецификатор отображает имена всех битовых составляющих заданного значения, если, конечно, это допустимое значение
g То же, что и G
X Отображает значение в виде шестнадцатеричного целого числа. Для отображения как минимум восьми цифр форматируемое значение дополняется (при необходимости) начальными нулями
X То же, что и X

В приведенной ниже программе демонстрируется применение спецификаторов формата перечислений.

// Отформатировать перечисление.
using System;

class EnumFmtDemo {
    enum Direction { North, South, East, West }
    [Flags] enum Status { Ready=0x1, OffLine=0x2,
                        Waiting=0x4, TransmitOK=0x8,
                        ReceiveOK=0x10, OnLine=0x20 }

    static void Main() {
        Direction d = Direction.West;

        Console.WriteLine("{0:G}", d);
        Console.WriteLine("{0:F}", d);

        Console.WriteLine("{0:D}", d);
        Console.WriteLine("{0:X}", d);

        Status s = Status.Ready | Status.TransmitOK;
        Console.WriteLine("{0:G}", s);
        Console.WriteLine("{0:F}", s);
        Console.WriteLine("{0:D}", s);
        Console.WriteLine("{0:X}", s);
    }
}

Ниже приведен результат выполнения этой программы.

West
West
3
00000003
Ready, TransmitOK
Ready, TransmitOK
9
00000009