Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 226
Текст из файла (страница 226)
Задание формата даты с)аяя Ра1е 1огта1 ( я1а11с сЬаг/т1() е соля! сЬаг' сиге; сопя1 сЬаг* сигг епд; риубе Ра ге /огепа!(): сиге(/т1), си гг ееиЯтЕ+ яег)ел(/т/)) ( ) //форлеат по умолнанию // указатели на текущий формат соем1 сЬаг' Ьеу!и() сопя1 ( ге1игп сигг; ) еопя1 сЬаг*епд() сопя!( ге!игл сигг епд; ) со!две!(сопя! сЬаг* р, сопя! сЬаг' у) ( сигг= р; сигг елд= у; ) оо(д яеЕ(сопя! сЬаг'р) ( сиге = р; согг епд = сиге+ ягг!еп(р); ) я1апс сопя1 сЬаг де/аи!1 Гт1() ( геЕигл/тЕ; ) )! сопя1 сйагРа1е /огтаЕХтЕ(Е= '%А,%В%0,%У"; //например, гггдау, Реьеиагу 5,!999 РаЕе /огтаЕдаге )т1; С++ не определяет стандартный формат вывода дат (%х является наиболее вероят- ным претендентом; () Г.4.4.3!.
Но даже если бы стандарт и суецествовал, нам бы на- верняка понадобились альтернативы. Их можно обеспечить, предоставив «формат по умолчанию» и способ изменить его. Например: 99б Приложение Г Локализация Чтобы иметь возможность воспользоваться форматом я1г/1!те() (5 ГАА.З), я воздержался от параметрнзации класса Ра1е ~огта1 используемым типом символов. Таким образом, данное решение допускает только те формы представления дат, формат которых может быть выражен как сйагЦ. Кроме того, я воспользовался глобальным объектом форматирования (с(а1е /т1) для предоставления формата Ра1е по умолчанию. За счет того, что значение с(а1е ~т1 можно изменять, мы получаегя грубый способ управления форматированием Ра1е, подобный тому, как для управления форматированием может использоваться у(оЬа(() Я Г.2.3).
Более общим решением было бы определение фасетов Ра1е !и и Ра1е ои1 для управления чтением и записью в поток. Это подход демонстрируется в 9 Г.4.4.7. При наличии Ра1е /огта1, Ра1езорега1ог«() можно написать следующим образом: 1ен!р!иге<с!азз СЬ, с!азз Тг Ьая!с ояггеатчСЬ, Т й орега1ог (Ьая!с оя1геат<СЬ, Тг>й я, солт Ра1ей д) // эип ась в фор>нате, эаданнон польэователен ( 1урепатеЬак!с оя1геатчСЬ, Тг экеп1гууиагг!(к) //слс б 2!.3.0 !Г(!уиагс/) ге1игп з; 1т 1; д.таус Пп(й1); 1гу ( сопя! Вте ри1чСЬ>й/= ике /псе!<1!те ригчСЬ»(з.уе1!ос()). (Щриг(к, з, зЯ!!(), й1, Йа1е ~т1 Ьедт(), с!а1е ~т1 епд())/а!!ес(()) я.яе1кса1е(юя ваке; Га!!Ь11) ) са1сЬ (...) ( !сапу!е !оехсерг!оп(я) // сн. 2 Т С2.2 ) ге1игп з; Я мог бы воспользоваться Ьаз ~асе1 чтобы удостовериться, что в локализации имеется фасет 1!те ри1<СЬ>.
Однако в данном случае про!це выглядит вариант с перехватом исключения, сгенерированного ияе /асе1. Приведем простую тестовую программу, управляющую форматом вывода при помокни аа1е /т1: !п1тат() 1гу ( Ра1е нЫ; тЬВе (с!и» сЫйй сЫ!= Расе()) сои1 «сЫ«епс!1; ,!! запись оформите // по у,чолчанию 0о1е /т! с!а1е ~тг.зе1('%Т/ч т/%д').
тЫ!е (с!и» сЫ йй сЫ ~= Расе()) сои1 «сЫ «епс!!; // запись в фаржа)пе // о у/ о' /о'д ) са1сЬ (РасеэВад Иа1е е) ( сои1« 'перехвачена неправильная дата: ' «е соду епг!!; Г.4. Стандартные фасеты 997 Г.4.4.7. Фаоет ввода даты Как всегда, ввод немного сложнее вывода. Однако ввиду того, что интерфейс к низкоуровневому вводу ограничен де1 с(а1е(), и благодаря тому, что определенный в 9 Г4.4.4 орегагог»() для лла1е не осуществляет непосредственного доступа к представлению 11а1е, мы могли бы воспользоваться этим орега1ог»() без изменений. Вот шаблонная версия, соответствующая орега1ог«(); гетр!а1ечс!азз СЬ, с1азз Тг тсгеатчС!ь Тг>й орега1ог»(!яггеат<СЬ, Тг й я, !за!ей с() ( 1урел а те!з!геат<СЬ, Тг> ззеп1гу уи асс!(я) Щуиагд) ге 1игп я; юз Ьаяес!ояса1е гея "0; 1тх=(0); !ясгеатЬиТ !1егагот СЬ, Т ел!1; 1гу ( ияе !асе1 Нте уе! <СЬ' >(яуе1!ос()).уе! йа! е(з, елл, з, гея, йх); са1сЬ (...) ( Йалс(!е !оехсерс!ол(я) ге1игп з; !/ см.
У 1:4.2.2 0 = !гасе(х.ст тс(ау, Ва1есМоп16(х.ст топ+!), х.1т уеагь!900, х.гт ин!ау); (Г(гея == !оз ЬакеэЬадЫ1) я.кегкга1е(гея) ге1игп я; Этот оператор ввода лла!е вызывает уе! с!а1е() фасета 1!те уе! из потока !з1геат. Зна- чит можно обеспечить иную и более гибкую форму ввода путем определения нового фасета. производного от !!те уе1: 1е~лр!а ге<с!аяз СЬ, с1азя 1л = 1я1геат6~4' Пега1огчСЬ' > с1азз Ва1е !л: риЫ!с кгс!э1!те уегчСИ, Й> ( ри6!1с !лаге ю(иее 1г=у): ягс!с1!те уе1<СЬ !п>(г) () рго1ес1ес(; !и с!о уе1 доге(Тп Ь, !не, !оя Ьакейз, !оз Ьазеэгояса1ейг, 1т'1тр) сопя!, рг!оаае елит Иуре (поиа1ие, ипупоюл, с!ауоЯюееЬ, топ!6)Я нет значения, неизвестное 1! значение, день недели, месяц Тл уе!иа!(Тл Ь, Тл е, !оя Ьазей з, юз Ьазесюяга1ей г, !и!" о, ЪЧуре* гея) сопк1; Функция де!па!() должна прочитать год, месяц, число, возможно день недели и трансформировать полученное в 1т.
Названия месяцев и дней недели зависят от локализации. Следовательно, мы не можем упоминать их непосредственно в нашей функции ввода. Так что мы будем распознавать месяцы и дни недели путем вызова функций, которые 1!те де! предоставляет для этой цели: де! топ!ипате() и де! юееЫау() Я ГАА.4). Приложение Г Локализация 998 Год, число и возможно месяц, представлены в виде целых чисел. К сожалению, на числе не написано, означает ли оно день, месяц илн еще что.
Например, 7 может означать «июль», или «7-е число», или даже < 2007 год». Для разрешении подобных двусмысленностей предназначена с!а!е огс!ег() нз !!те де!(). Стратегия Эа!е гп состоит в чтении значений, их классификации и вызове с(а!е огс!ег(), чтобы узнать, имеют ли (и какой) смысл введенные значения. Закрытая функция де!оа!() осуществляет непосредственное чтение буфера потока ввода и начальную классификацию: !етр1а!с<с!азз СЬ, с(азз 1п> !и Ва!е т< СЬ, 1п>:ие(ва!(1п 6, 1п е, !оз база з, !оз Ьазес!оз!а!ей г, !п~ о, Иуре' гез) сопя! // Чил!иел~ часть даты: «чело, день недели или месяц. //Пропускаем заполнигпели и пунктуацик~.
сопя! с!уре< СЬ>$ с!= изе ~псе!< с!уре< СЬ»(з.уе!!ос()); // с!уре определен в Л4.5 СЬс; *гез = пооа1ие; // значение не наидено УоГ(х) ( // пропускаел~ заполнтпели и пункпуациюо !/'(6 == е) ге!игп е; с =*Ь; !Яс!!з(с!уре Ьазеззрасе, с))) с!!з(с!уре Ьазегрипсс, с))) Ьгеау; ++Ь; (/(с! !з(с!уре базе. 0)у!1, с)) ( // читаеи целые, игнорируя питрипс! !п!!= 0; г(о ( // преобразуел~ цифру из произвольного символьного типо ~ //в ее числовое значение: з!айс сааг сонь! д(у!!з[) = '0123455789', ! = 0 10 «1!пд(д(д(!з, д(у!!з»10, с!.паггои(с/ ')) — г((у!!з; с='++Ь; ) и>Ь!!е (с!.!з(с!уре Ьазезйд!1, с)); *о = 0 гез = ипйпочвп; //целое, но неизвестно, что оно означает ге!игп Ь, !/(с!дз(с!уре Ьазеза)рЬа, с)) ( //поиск названия лесяца или дня недели Ьаз/с з!гту< СЬ> з!г; и61!е(с!!з(с!уре Ьазеза(рЬа, с)) ( // читоея силсволы в строку з!ге= с; (/(-н-6 == е) Ьгеай; с =*Ь; !т 1; Ьаз!с з!гтуз!геат< СЬ> зз(зл~; !уреде//з! еатЬи/ пега!юг< СЬ> Ы; // тип итератора для буфера // погпока зз Г4.
Стандартные фасеты 999 де! топ16пате(зк.гйбиЯ), Щ к, г, йг); // читаея иэ находящегося в палтти // буфера потока (/((гй(!ок ЬазесЬайЬ!Г)!ок ЬазтХа1!ЫЯ==О) ( *о= 1.1сп топ; *гез = топЯ; г=О; ге!игл Ь; г=О; // очищаеи состояние перед попыгпкой повп|орного чтения уе1 тееудау(зз.гйбиЯ), Я(), з, г, йг); // читаем из находящегося в паяяпт // буфера потока (/((гй!оз ЬазечЬадЫгг==О) ( *о = 1. 1т иэдау; *гез = йауо/тее/с, г=О; ге/игп Ь; ) г)= соз базе;~а!1Ы1; гегигп Ь; ) 1етр!а1екс!акз СЬ, с1азк /п > 1и /ла1е !п< СЬ, рп>сйо увг йоге(1п Ь, 1п е, 1оз Ьазей з, соз Ьазе:доз!а!ей г, гт* 1тр) сопз1 // несбяэательный день недели, эа которым следует утд, дту, тйу или ус!т 1п1 и а![3;с Муре гез[3) =( поиа1ие); // для значений числа, л~есяца и гада //в некоторая порядке // для классификации эначенис! /ог(!а!1=0, 6!=в йй!<3!+ь!) ( // прочитать число,несяц и гад Ь = уеюайЬ, е, к, г, 'иа1[!], *гез[с)); (7(г) ге!ига Ь, // огиибка Главный трюк в том, чтобы отличить месяцы от дней недели.
Мы читаем через итера- торы ввода, поэтому мы не может прочитать [Ь,е) дважды, пытаясь прочитать первый раз месяц, а второй — день недели. С другой стороны, мы не можем принять решение, имея информацию в каждый момент времени только об одном символе, потому что только де1 топ!/квоте() и де1 шее/сс(ау() знают, какая последовательность является названием месяша и дня недели в данной локализации.
Решение, которое я выбрал, состоит в чтении строк символов в строку з1ппд, создании потока з1г!пдз1геат из этой строки и повторном чтении из буфера к1 еатЬи/'созданного потока. Для фиксации ошибок непосредственно устанавливаются биты состояния, такие как юк ЬазесЬас!Ы1. Это необходимо, потому что более удобные функции манипулирования состоянием потока, такие как с!еаг() и зе1 з1а1е(), определены в Ьаз/с юк, а не в его базовом классе юк базе (9 2133). Затем при необходимости оператор» использует информацию об ошибках, сообщенную де1 с(а1е(), для переустановки состояния потока ввода. Наличие де1оа!() позволяет сначала прочитать значения и только затем проверитгь осмыслены ли они.
Функция с(а1е ога!ег() может иметь решающее значение: Приложение Г Локализация !000 (/(сея[1]==попа!не) ( г[ !оя ЬазесЬайЬЬ, ге1игп Ь; ,1/ неполнип дата ) !/'(сея[1]==йауо/!пееЬ) ( 1тр — 1т шйау= иа![1]; -1; // осторожен: не день, не месяц и не год 1!те Ьаяезйа1еогйегогйег= баге огйег(); // теперь попытаемся придать // смысл пропитанным значениям с/[сея[0) == топ1Ь) [ //-. // тйу или ошибки е!зе !/(сея[!] == топ1Ь) ( 1тр- 1т топ иа![1]; зш!1сЬ(огйег) ( саяе сбпу; 1тр->1т тйау = иа1[0]; 1тр->1т уеаг = иа1[2]; Ьгеау; саяе утй: 1тр->1т уеаг= иа![О]; 1тр- 1т тйау= иа![2]; Ьгеау; йе/аи!Ь г)= !оя Ьазесбайб!1; ге1игп Ь; ) еЬе !Г'[сея[2] == топ! Ь) ( // -. ) е!зе ( 0-. // уйт или ошибки // соответствует йа!еотег или ошибка // привести базовьсй год в соответствие // соглашениям 1т 1тр — 1т уеаг — = 1000; ге1игп Ь; ий тат() 1~0( с!и 1тбие(!ос(!оса1е[), пете!а!е 1п)) // нтенпе дат с помои(ью Па1е !и Я опустил фрагменты кода, не имеющие отношение к демонстрации локализаций, дат и управления вводом.