Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 227
Текст из файла (страница 227)
Названия месяцев и дней недели зависят от конкретного контекста локализации. Следовательно, мы не можем упоминать их непосредственно в нашей функции ввода. Вместо этого мы будем распознавать месяцы и дни посредством вызова функций, которые фасет «те ~е! предоставляет для этой цели: яе! гпопгйпагпе() и ее! неейг!ау() 6Г).4.4.4). Год, день месяца и, возможно, месяц представляются целыми числами.
К сожалению, само число не указывает, относится ли оно к месяцу, дню или к чему-либо еше. Например, 7 может означать июль месяц, или 7-е число, или даже 2007 год. Настоящей целью функции Ыа!е оЫег() из фасета «те яе! является разрешение таких двусмысленностей. Стратегия фасета Эа!е 1« состоит в том, чтобы читать значения, классифицировать их и вызывать ь!а!е огь!ег(), чтобы узнать, имеют ли смысл (и какой) введенные значения. Закрытая функция яеяиа1() осуществляет непосредственное чтение буфера потока ввода и начальную классификацию: гетр1аге<с1аяя СЬ, с1аяя 1п> 1и Роге !п<СЬ,1п>.":легка!(1п Ь, 1п е, !оя Ьаяеь я, !оя Ьаяе::!аз!а!ел г, !пг* г, ггбре* гея) сопя! // Читаем части Ро(е: число, день недели, или месяц.
Р Пропускаем пробелы и пунктуацию. ( сопя! сгуре<СЬ>а сг = ияеуасег<сяуре<СЬ» (я.еег(ос() ); П стуре - см. яР 45 Сй с; // значение не найдено *гея = пога1ие; // пропускаем пробелы и пунктуацию рог(;; ) ( (1'(Ь == е) ге!игл ет с=*Ь; (1(! (с!.Ь(стуре Ьаяе::красе,с) ( ( с!.!я(стуре Ьгпе::риис!,с) ) ) Ьгеай; ++Ь; ) гз (с!.и (стуре Ьаяе::4!0!г, с) ) //читаем целое, не обращая внимание на питрипс! ( гп! ! = 0; до //переводим цифру из произвольного символьного типа в десятичное значение.
( яга«с сйаг сопяг йуйя() = ч0123456789"; 1= !*10 +7)пд(4!0!«,4!е!«+10, с!.паггои (с, ' ' ) ) — 4!0!гзт с= *++Ь; ) и й!!е (сг. Ь (с(уре Ьаяе:: йу!я, с) ) т *и=(т *гся = ипйпоыпт //целое значение, но неизвестно, чему оно соответствует гегигп Ьт Приложение (). Локализация 1060 ф'(с!. 0 (ссуре Ьазе:: а(рйа, с) ) ( Ьазйз ззггпе<СЬ> мг; ый(!е (с!.0 (сзуре Ьазе:: а!рйа, с) ) ( ззг+= с; (Г(++Ь == е) Ьгеайг с= *Ьг ) УУ поиск названия месяца или дня недели УУ читаем символы в строку пп з; Ьаз(с в!с!идя(геат<СЬ> ев (в(г) ) (уреаеу (зггеатйиу иегагог<СЬ> о1г УУ итераторный тип для буфера вз де< топейпате(вз.гйЬиГ(),Ы(),з, г, а!) г У читаем из буфера потока в памяти (Г( (га (Ьзз Ьазе:: ЬаЕЬ(! ( (оз базе::уаггйг!) ) ==О) ( *1= !.(т топ) *гез = топгй! гееагп Ь; ) г=О; /У очистка состояния перед повторным чтением Ое! кееййау (т.
гг(Ьиу (), ал (),з, г, а!) ! УУ читаем из буфера потока в памяти ф( (га ()оз Ьазе::ЬааЬгс((оз базе::Гаг(Ьг!) ) ==О) ( *г = !.Пп наау! *гез = НауоЛгеейг ге!ига Ь! ) г (= Ьзз Ьазе::Га)гЬиг ге(игп Ь; Главный трюк здесь заключается в том, чтобы отличить месяцы от дней недели. Мы читаем с помощью итераторов ввода, так что мы не можем дважды прочитать интервал [Ь, е), сначала отыскивая месяц, а затем день недели. С другой стороны, мы не можем принять решение, глядя лишь на один символ, потому что только функции яе! топайпагпе() и яе! )вееййу() знают, какая последовательность символов означает название месяца и дня недели в данной локализации.
Я принял решение читать строки алфавитных символов в тип згг!пя, создать поток згг(пяз(геат из ззг(пя, после чего вычитывать уже ззгеагпйиГэтого потока. Обработка ошибок состоит в непосредственном установлении битов состояния, таких как (оз Ьазе:: Ьа!!Ь(!. Это необходимо из-за того, что более удобные функции манипулирования состоянием потока, такие как с!еаг() и зе! з1аге(), определены в Ьаз(с !оз, а не в его базовом классе )оз Ьазе (521.3.3). При необходимости операция» использует информацию об ошибках, получаемую от ее! йоге(), для переустановки состояния потока ввода.
Располагая функцией лагоа!( ), мы можем сначала прочитать значения, и лишь после этого проверять, имеют ли они смысл. Функция йае озз1ег() критически важна: [).4. Стандартные фасеты 1001 <етр!а<е<с<ат СЬ, с<язв 1п> 1и Вате т<СЬ,1и>::йо де< йа<е(1п Ь, 1п е, <оя Ьаяеь я, <оз Ьаяе: <(оя<а<еь г, <т* йир) сопи //необязательный день недели, за которьин идут утй, йту, тйу, или уйт ( ии га1[З) < 'г)уре гея[З] = (позе!ив) < <ог(1и< 1=0< Ь! =е ьь «3< ++<) //читаем йау, топ<Ь и уеаг ( Ь = йе<га1(Ь,е,я, г, ьиа1[1], ьгез [1] ) < (!'(г) ге<ига Ь< // оорз: еггог (!'(гея [<] ==поги!ие) // незавершенная дата ( г ) = Ьзя Ьаяе::ЬайЫ« ге<ига Ь; ) й' (гея [<] ==йауо/1еееЬ) ( <тр-><т <гйау = га1 [1] < ) // оорз: не йау, топ<0 или уеаг йте Ьазе: <йа<еогйег огйег = йа<е огйег(); // попытаемся придать смысл // прочитанным значениям // тйу или еггог //йту или утй или еггог ) ) е!яе (1'(гея [2] ==топ<0) //уйт или еггог (!(гея [О) ==топ<0) ( //...
) еЬе 0 (гея [1] ==топей) ( йир->йп топ = га1 [1] < яюйсЬ (огйег) ( саяе йту: <тр-><т тйау = га1 [О] < йир->т< уеаг = го< [2) < Ьгеаь< саяе утй < <тр-><т уеаг = ге![0]; <тр-><т тйау = га1 [2] < Ьгеаь< йеуаи0: г (= <оя Ьаяе«ЬайЬ«; гей<гй Ь; // для йау, топ<0 и уеаг в некотором порядке //для классификации значений 1Об2 Приложение О.
Локализация // полагаемся но г/агеогдег или еггог е1зе ( //... // подгоняем базовый год под гт-соглашения гтр->гт уеаг =- 1900; ге/игл Ь; ) Я опустил часть кода, которая не вносит ничего нового в понимание контекстов локализации, дат, или же обработки ввода. Оставляем в качестве упражнения написание более универсальных функций ввода дат 612.б[9-10]). Вот простая тестовая программа: Ьи тати () пу ( сш.ипЬие(!ос((оса!е(),пеы Расе ш) ); //нигпаем даты, используя Ра(е и иЬ11е (сш» ~Ы а ь дд ! = Расе () ) сои( << ~Ы << епд1( саге/г (Расе:: Ва~Г Васе е) ( сонг «нЬад дале саиВЬ(: " «е.нйу « епд1( ) Заметьте, что Ио яег Фаге() примет и бессмысленные даты, такие как Тйиглдау ОсгоЬег 7, 1990 1999/сеЬ/31 Проверка согласованности года, месяца, числа и, возможно, дня недели, выполняется в конструкторе Риге.
Именно класс Риге должен знать, что является корректной датой, а Роге Ьи () не обязан разделять зто знание. Можно написать Ве(еа! О или Ыо яет йоге () таким образом, чтобы они пытались угадывать смысл числовых значений. Например, очевидно, что 12 Мау 1922 не означает 1922 число 12 года. То есть мы можем догадаться. что число, которое не может быть месяцем, является годом. Подобного рода догадки бывают полезными в конкретных ограниченных смысловых контекстах.
Но в более общих случаях польза от таких догадок сомнительна. Например: 12 Мау 15 может соответствовать любому году из следующих: 12, 15, ! 912, 1915, 2012 или 2015. Часто бывает полезным снабжать числа подсказками. Например, 1-ое и 15-ое — зто дни, а "751 до и.э." и "1453 и.э." — зто годы. 1063 [).4. Стандартные фасеты 0.4.5. Классификация символов При чтении символов часто возникает задача их классификации с целью придания смысла прочитанному. Например, чтобы прочесть число, процедура ввода должна знать, какие си)ивовы являются цифрами, Это похоже на пример из 96.1.2, который демонстрирует классификацию символов стандартными классифицирую- шими функциями с целью разбора вводимых выражений. Естественно, классификация символов зависит от используемого алфавита.
Как следствие, фасет стуре используется для классификации символов в рамках контекста локализации. Символы классифицируются в соответствии с перечислением таз!с // реальные значения зависят от реализации Тип таей не зависит от конкретного типа символов. Как следствие, это перечисление помещается в нешаблонный базовый класс. Очевидно, что таз!с отражает традиционную для С и С++ классификацию 620.4.1). Однако для разных наборов символов, разные символы попадут в разные группы. Например, для набора АБСП целое значение 125 соответствует символу ' ) ', означающему знак пунктуации (риис!). А в датском национальном наборе символов этот числовой код соответствует гласной 'а, которая в датской локализации должна классифицироваться как а1ргюа.
Данная классификация получила название птаз)г (маска), поскольку традиционной эффективной реализацией для классификации небольших наборов символов является таблица, в которой каждый вход (строка, элемент) хранит битовое представление классификации. Например: гаЫе[ 'а' ] == 1оыег ) а1рьа ) хдге!ю юаЫе['1'] == 4т!1 юаЫе [ ' ' ) = = юрасе При такой реализации гаЫе[с] ьт отлично от нуля, если символ с есть т, и 0 в противном случае. Фасет стуре определяется следующим образом: с1аюю юга:: стуре Ьаюе [ риЫзс: епит таюЬ [ юрасе = 1, рппю = 1«1, юг! = 1«г, иррег = 1«3, !опег = 1«4, а1рла = 1«з, 4!я!( = 1«б, риною = 1« 7, 1е!ю = 1«В, а1пит=а!рва ) 4!е11, егарл=а!пит ) риис! )' )! // пробельные символы (в "С" !осо!е: ",')и',')!', ...) // символы печати // управляющие символы // символы в верхнем регистре //символы в нижнем регистре // алфавитные символы // десятичные цифры // символы пунктуации // изестнадцатеричные цифры // алфавитно-цифровые символы 1064 Приложение Р Локализация <е<пр<а<е<с1ат СЬ> с<ам зЫ<: с<уре < риЫЫ !оса(е;:(осе<, риЫ<с с<уре Ьазе ( риЫ1с < <уре«е)' СЬ слог <уре; ехр1(с(< с<уре (я<ге < г = О) < // "с" есть "т"? Ьоо1 т(таял т, СЬ с) сопя« //Упоместить классификацию каждого СЬ из (Ь:е) в гк сопя< СЬ* Ы (сопз< СЬ* Ь, сопел СЬ* е, таял* г) солЮ сопя СЬ* звал (я(таял т, сопя< СЬ* Ь, соля< СЬ* е) сопел< //найти т, сот< СЬ* ясал ло<(таей т, соля< СЬ* Ь, сопи СЬ* е) сопя« //найти не-т СЬ Ыиррег(СЬ с) сот« соля< СЬ* <оиррег(СЬ* Ь, сопя< СЬ* е) соляе! СЬ <о!он е< (СЬ с) сопя« сот< СЬ* <о!олег(СЬ* Ь, сот< СЬ* е) сопя; // сопгег< !Ь:е) СЬ вЫел(слог с) сопя« сопле сааг* пЫеп (сопя< слег* Ь, сот< слег* е, СЬ* Ь2) сопя<! слог лассо<я(СЬ с, слаг йеЯ сопя« сопи СЬ* пагго<г(сопя< СЬ* Ь, вол<а СЬ* е, слег де)", слаг* Ь2) соты я<або <оса1е::Ы!И! //обьект идентификации фасета (бР2,,бРЗ, 3Р.З.!) рго<ес<е«< -с<уре() < //виртуольные 'Уо "-функции (см.