Сегодня я расскажу о том, как формировать строки, помещая в них значения переменных различных типов. Я думаю, вы знаете, что любое значение можно преобразовать в строку через метод ToString(). Например, так:
var a = 10.50; var message = "Сумма = " + a;
Но, используя такой метод, вы можете получить строковое представление значения только в одном формате, определенном вашей текущей культурой (CultureInfo).
Сейчас мы рассмотрим, каким образом можно задавать формат для значений при формировании строки. Для этого в .NET есть встроенная возможность, представленная “записывающими” методами различных классов. К таким, например, относятся:
System.String.Format
Console.WriteLine
StreamWriter.Write
Я буду рассматривать форматирование на примере String.Format, но то же самое можно использовать и в остальных методах. Выглядит это так:
var a = 10.50; var message = string.Format("Сумма = {0}", a);
Метод String.Format принимает первым параметром шаблон строки, в которую подставляются форматированные значения. Шаблон содержит маркеры – фигурные скобки, которые указывают, куда необходимо подставить значения. В простейшем случае этот маркер содержит одно число – номер параметра, который надо подставить (0 – соответствует первому параметру).
В следующем коде у меня производится формирование строки из двух параметров:
var a = 10.50; var dt = new DateTime(2011, 09, 01); var message = string.Format("Сумма = {0}, Дата = {1}", a, dt);
Но, как я уже сказал, мы также можем влиять на формат значений, которые вставляются в строку.
var a = 10.50; var dt = new DateTime(2011, 09, 01); var message = string.Format("Сумма = {0:c}, Дата = {1:d}", a, dt);
В этом примере я внутри маркера задал форматы для обоих значений: для суммы я указал формат Currency (“:c” внутри маркера), для даты – “краткий формат даты” (“:d” в маркере).
Это примеры встроенных в .NET форматов, и конечный результат их применения зависит от текущей культуры (CultureInfo), которая по умолчанию соответствует настройкам Windows. Если у вас в Windows выбраны российские региональные настройки, то в данном примере сумма выведется в рублях, а дата в российском формате даты.
Иногда бывает необходимо преобразовать набор чисел в строки таким образом, чтобы все строки были выравнены по левому или правому краю и имели равные длины. Числа при этом дополняются пробелами слева или справа:
var a1 = 1234; var a2 = 12345; Console.WriteLine(@"{0,5:d}", a1); //по правому краю Console.WriteLine(@"{0,5:d}", a2); //по правому краю Console.WriteLine(@"{0,-5:d}", a1);//по левому краю Console.WriteLine(@"{0,-5:d}", a2);//по левому краю
В этом примере внутри шаблона указывается дополнительный параметр, через запятую после индекса параметра. Этот параметр указывает минимальное число символов, которое должно быть в строке. Если это положительное число, то строки выравниваются по правому краю, если отрицательное – по левому.
Встроенные форматы
Кроме приведенных форматов существует еще много других встроенных форматов:
Код
Тип
Описание
c/C
Валюта
d/D
Цифровой
Только для целых типов, выводит целое (с “-“ для отрицательных)
e/E
Научный
Выводит число в экспоненциальном формате. Регистр буквы “E” влияет на регистр этой буквы при форматировании
f/F
Фиксированная точка
Выводит целое или дробное число
g/G
Общий
Выбирает формат в соответствии с типом значения и размером значения – научный или фиксированная точка.
n/N
Разделители групп разрядов
Выводит целое или дробное число, но при этом отделяет группы разрядов
p/P
Процент
Умножает число на 100 и выводит со знаком процентов
r/R
Обратный
Только плавающая точка и BigInt; переводит в строку таким образом, что значение можно без потерь точности преобразовать обратно
x/X
Шеснадцатиричный
Только для целых, переводит в Hex-формат. Регистр буквы X влияет на регистр символов в выходной строке.
var a = 1234.34542; Console.WriteLine(@"{0:N3}", a);
В этом примере число указывает число символов после запятой.
Теперь посмотрим, какие есть встроенные форматы дат:
Код
Тип
Пример
d
Короткая дата
01.09.2011
D
Длинная дата
1 сентября 2011 г.
t
Короткое время
10:20
T
Длинное время
10:20:30
f
Длинная дата/короткое время
1 сентября 2011 г. 10:20
F
Длинная дата/длинное время
1 сентября 2011 г. 10:20:30
g
Короткая дата/короткое время
01.09.2011 10:20
G
Короткая дата/длинное время
01.09.2011 10:20:30
M/m
Месяц и день
сентября 01
o/O
Обратный
2011-09-01T10:20:30.0000000
r/R
RFC1123
Thu, 01 Sep 2011 10:20:30 GMT
s
Для сортировки
2011-09-01T10:20:30
u
Локальное, в универсальном формате
2011-09-01 10:20:30Z
U
GMT
1 сентября 2011 г. 6:20:30
Y
Год и месяц
Сентябрь 2011
При преобразовании чисел в строку при помощи встроенных форматов .NET использует правила, которые заданы в текущем CultureInfo.
CultureInfo влияет на множество аспектов – символ валюты, символы разделителей, символ “-”, количество знаков после запятой по умолчанию. То же самое и с датами. CultureInfo определяет символы разделителей, имена месяцев и дней недели, форматы времени.
Таким образом, если вы используете для форматирования значений только встроенные форматы, то вы автоматически получите глобализацию преобразований чисел и дат в строки. В каждой стране это преобразование будет происходить по своим правилам.
С другой стороны, если вам необходимо при преобразовании применить другую культуру, то это, как правило, можно сделать, используя соответствующую перегрузку метода.
var a = 10.50; var dt = new DateTime(2011, 09, 01); var info = new CultureInfo(1033); var message = string.Format(info, "Сумма = {0:c}, Дата = {1:d}", a, dt);
В этом примере я применил другую культуру для преобразования значений. Свой формат
Если нас не устраивает встроенный формат, то можно задавать формат самостоятельно. Суть этого процесса в том, что вы с помощью специальных символов и обычного текста создаете строку формата, эти символы я дальше покажу в таблице, а пока пример.
Я произведу форматирование серийного номера, который, например, в базе данных лежит в виде целого Int32:
var message = string.Format("Серийный = {0:000-000-000}", a); Console.WriteLine(message);
Здесь у меня внутри маркера 000-000-000 – это шаблон, в нем каждый 0 – это специальный символ цифры (вставляется цифра, если она есть, либо ‘0’), дефис – это просто промежуточный текст.
var a = 2345.43; var message = string.Format(@"Сумма = {0:#.00р\.}", a); Console.WriteLine(message);
Здесь строка формата “#.00р\.”. В ней символ “#”, который я могу опустить, означает цифры (до запятой в этом примере), “.” – это разделитель дроби, “00” – означает “взять и показать две цифры после запятой”, “р” – просто текст – рубли, “\.” – просто точка; я использовал символ “\” перед точкой, т. к. точка – специальный символ.
Вот краткое описание специальных символов, которые можно использовать в своих шаблонах:
Код
Тип
Пример
Результат
Описание
Исходное число
1234,567
0
Цифра или ноль
00000.0000
01234, 5670
Если нет цифры, то вместо маркера ставится 0
0.00
1234,56
#
Цифра
#####.####
1234,567
Если нет цифры, то вместо маркера ничего не ставится
#.##
1234,56
.
Разделитель дроби
0.#
1234,5
Интерпретируется только первая из точек
,
Разделитель тысяч
00,0 или 0,000 или 00,0,000
1 234
Интерпретируется только первая из запятых. Слева и справа от запятой должны быть “0” или “#”, но не обязательно рядом
,.
Масштабирование
0,.0
1,2
Число уменьшается в 1000^N раз, где N – число запятых, точка позволяет задать дробную часть.
0,
1
Если дробной части нет, запятые ставятся в конце
0,,.000
0,001
%
Процент
0%
123456%
Умножает на 100 и добавляет знак процента
e
Экспонента
00.0e+00
12,3e+02
Научный формат, цифры после “E” задают число разрядов порядка
;
Разделитель секций
0.##;(0.#);ноль
1234,56
Позволяет задать три секции: отдельно для положительных, отрицательных чисел и нуля
0.##;(0.#);ноль
(1234,56)
Для значения “-1234.56”
0.##;(0.#);ноль
Нуль
Для значения “0”
\
Escape
\.##.#\.
.1234,5.
Дает возможность вставлять спец-символы как обычный текст
Специальные символы можно комбинировать. К примеру, шаблон “000,0,.##e+00;(0.#);ноль” даст результатом для числа “123456789.1234567” строку “1234,57e+02”.
Число положительное, поэтому будет использоваться первая секция. Сначала будет произведено деление на 1000 (символы “,.”), затем приведение к экспоненте (“e+00”), при котором будет оставлено 4 цифры слева от точки (четыре нуля), затем будет произведено округление до сотых (“.##”), и в конце разделение тысяч (первая запятая).
Кроме специальных символов в шаблон можно вставлять обычный текст, так я вставил скобки в шаблон с разделителем секций. Текст можно вставлять в любое место.
Стоит обратить внимание еще на два момента:
1. Eсли количество цифр целой части больше числа символов “0” и “#” слева от точки в шаблоне, то целая часть выводится все равно полностью. При этом лишние левые цифры ставятся в соответствие крайнему символу “0” или “#”. Например, используя шаблон “(#)##” для числа 12345, получим строку “(123)45”.
2. Если в числе нет целой части, например в числе 0,34, то шаблон “#.##” даст результат “,34”. Чтобы перед разделителем дроби был ноль, необходимо слева от точки использовать маркер “0”, как, например, здесь “0.##”.
Теперь посмотрим, какой арсенал у нас имеется для форматирования дат своим форматом. Здесь такой же принцип – для построения шаблона используются специальные символы.
Код
Тип
Результат
Описание
Значение
01.01.2001 14:20:30.1234
y
Год 0-99
1
yy
Год 00-99
01
yyyy
Год, 4 цифры
2001
M
Месяц 1-12
1
MM
Месяц 01-12
01
d
День 1-31
1
dd
День 01-31
01
h
Час 1-12
2
hh
Час 01-12
02
H
Час 1-24
14
HH
Час 01-24
14
m
Минута 0-59
20
mm
Минута 00-59
20
s
Секунда 0-59
30
ss
Секунда 00-59
30
f – ffffff
Доли секунды
12 (для шаблона ff)
Количество f соответствует количеству разрядов долей секунды
F-FFFFFF
Доли секунды, если они не равны 0
12 (для шаблона FF)
Количество F соответствует количеству разрядов долей секунды
MMM
Сокращенное имя месяца
янв
MMMM
Имя месяца
Январь
ddd
Короткое имя дня недели
Пн
dddd
Имя дня недели
понедельник
tt
Маркер для “AM” и “PM” 12-часового формата
PM
zz
Смещение временное зоны, короткое
+03
zzz
Смещение временное зоны, полное
+03:00
gg
Эра
A.D.
:
Разделитель времени
14:20:30
/
Разделитель даты
01.01.2001
\
Escape
Дает возможность вставлять спец-символы как обычный текст
%
Custom Format
“%d” даст “1”
Указывается перед символом, чтобы указать, что это не встроенный формат
Разделители даты и времени – это специальные символы, при форматировании они будут заменены на соответствующие символы CultureInfo.
При формировании строки специальные символы и их последовательности можно размещать как угодно и совместно с обычным текстом.
Вот несколько примеров:
Что еще важно знать.
1. Те же самые строки форматов можно использовать, вызывая перегрузку метода ToString у чисел и дат.
var a = 10.50; var message = a.ToString("c");
2. Еще раз напоминаю, что CultureInfo влияет на то, как будут работать встроенные форматы. Но кроме того, CultureInfo влияет и на то, как ведут себя специальные символы и их комбинации во встроенных форматах.
Например, от этого зависят имена месяцев и дней недели, символ разделителя дат и времени, символы разделителя дроби и разделитель тысяч и др.
3. По возможности старайтесь использовать встроенные форматы. Это сделает ваше приложение более гибким.
До сих пор для кода примеров этой книги нами было написано множество классов, и обычно они реализовывали метод ToString () для того, чтобы отображать содержимое экземпляров этих классов. Однако довольно часто у пользователей возникает потребность отобразить содержимое переменных другим способом — в зависимости от национальных и культурных стандартов. Класс .NET System. DateTime представляет наиболее очевидный пример этого. Например, может понадобиться отобразить одну и ту же дату как 10 June 2007, 10 Jun 2007, 6/10/07 (США), 10/6/07 (Великобритания), или 10.06.2007 (Германия).
Аналогично, структура Vector из главы 3 реализует метод Vector. ToString () для отображения вектора в формате (4, 56, 8). Однако существует другой общепринятый способ записи векторов — в форме 4i + 5 б j + 8 k. Если вы хотите, чтобы написанные вами классы были дружественными к пользователю, они должны предлагать средства для отображения своихстроковых представлений в любом из форматов, которые могут понадобиться пользователю. Исполняющая система .NET определяет стандартный способ достижения этого — интерфейс IFormattable. Описание способа добавления этого важного свойства к пользовательским классам и структурам и представляет собой тему настоящего раздела.
Как вам известно, формат, в котором нужно отобразить значение переменной, следует указывать при вызове Console .WriteLine (). Таким образом, мы используем здесь этот метод в качестве примера, хотя большая часть обсуждения относится к любой ситуации, когда возникает потребность в форматировании строки. Например, если требуется отобразить значение переменной в окне списка или текстовом поле, то при этом для получения соответствующего строкового представления переменной обычно используется метод String.Format (). Однако действительные спецификато¬ры формата, применяемые для указания конкретного формата, идентичны тем, что передаются методу Console .WriteLine (). Поэтому мы сосредоточимся на примере Console .WriteLine (). Начнем с рассмотрения того, что происходит, когда форматная строка применяется к примитивному типу, а отсюда станет ясно, как следует включать спецификаторы формата для пользовательских классов и структур.
В главе 2 строки формата применялись в Console. Write () и Console. WriteLine () следующим образом:
double d = 13.45;
int i = 45;
Console.WriteLine("Значение double равно {0,10:E}, a int содержит {1}", d, i);
Сама форматная строка содержит большую часть отображаемого текста, но всякий раз, когда в нее должно быть вставлено значение переменной, в фигурных скобках указывается индекс. В фигурные скобки может быть включена и другая информация, относящаяся к формату данного элемента, например, как та, что описана ниже.
Количество символов, которое займет представление элемента, снабженное префиксом-запятой. Отрицательное число указывает, что элемент должен быть выровнен по левой границе, а положительное — по правой. Если элемент на самом деле занимает больше символов, чем ему отведено форматом, он отображается полностью.
Спецификатор формата предваряется двоеточием. Это указывает, каким образом необходимо отформатировать элемент. Например, можно указать, должно ли число быть форматировано как денежное значение либо его следует отобразить в научной нотации.
В табл. 8.3 перечислены часто используемые спецификаторы формата для числовых типов, которые уже кратко упоминались в главе 2.
Таблица 8.3. Часто используемые спецификаторы формата для числовых типов
1120 (если нужно отобразить 0x1120, то Ох нужно выводить отдельно)
Если целое число требуется дополнить нулями, можно воспользоваться спецификатором формата 0 (ноль), повторив его столько раз, сколько составляет требуемая длина. Например, спецификатор формата 0000 отобразит 3 в виде 0003, 99 — в виде 0099 и так далее.
Мы не можем привести здесь полный список, поскольку другие типы данных добавляют свои собственные спецификаторы. Целью настоящего раздела будет показать, как определять собственные спецификаторы для пользовательских классов.
Измерить время выполнения можно так using System.Diagnostics; ... ... ... Stopwatch sw = new Stopwatch(); sw.Start(); ... Измеряемый Код sw.Stop(); MessageBox.Show(sw.Elapsed.Minutes + "Min " + sw.Elapsed.Seconds + "Sec " + sw.Elapsed.Milliseconds + "ms");
При своем запуске приложение пытается создать мьютекс с определенным именем. Если это удалось, то запускаемый экземпляр приложения является первым и пока единственным. В противном случае Mutex не позволит вновь запустить пока еще работающее приложение Поэтому, данный класс мы и используем в нашем случае:
using System; using System.Windows.Forms; using System.Threading; namespace OneCopyOfApp { static class Program { private static Mutex m_instance; private const string m_appName = "NameOfMyApp";
[STAThread] static void Main() { bool tryCreateNewApp; m_instance = new Mutex(true, m_appName, out tryCreateNewApp); if (tryCreateNewApp) { Application.EnableVisualStyles(); Application. SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); return; } } } }
Есть второй, более безопасный в отношении атак способ. Основан он на библиотеках VB. Для начала добавьте ссылку на сборку Microsoft.VisualBasic.dll.
Для удобства вынести в начало проекта пространство имен Microsoft.VisualBasic.ApplicationServices. Ну а сам код следующий (обратите внимание каким образом мы вызываем метод Run())