Саммерфилд - Программирование на Python 3 (1077331), страница 80
Текст из файла (страница 80)
Это может быть строка с идентификатором, только если словарь бата пуст (потому что он пуст изначально и очищается после окончания чтения каждой следующей записи) и если строка начинается с символа «(» и заканчивается символом «)». Если эти условия соблюдаются, идентификатор отчета помещается в словарь бата. После этого условие в данной ветке е119 не будет возвращать Тгое, пока словарь с[аСа снова не будет очищен. е1ст "=" 1п 1спе: Кеу, ча1ое = 11пе.вр1ст("=", 1) 1г кеу == "басе": баса[кеу] = басессве.басессве.всгрсспе(ча1ое, тяу-96в-об").бате() 360 Глава 7. Работа с файлами е1тт Кеу == "р!1о! регсепт поогв оп туре": оата[кеу] = гтоат(ча1ое) е1тг кеу == "р11от тота1 поогв": оата[кеу] = тпт(ча1ае) е1тт Кеу == "з!Оатг": пата[Азу] = ЬооИ1пт(ча1ое)) е1ве: оата[кеу] = ча1ае е)тт 1!по == ".ИАННАТ1УЕ ВТАНТ.": паггат1че = "" е1ве; гатов кеуеггог("рагв1по еггог оп 1«пе (0)".гогват( 1!по)) Если метод находится не в процессе чтения комментария и был прочитан не идентификатор нового отчета, остаются всего три возможных варианта: был прочитан элемент ключ значение, был прочитан маркер начала комментария или что-то пошло не так.
В случае, если была прочитана строка ключ=значение, мы разбиваем ее по первому вхождению символа «=«, указав, что число разбиений не должно превышать одного, — это означает, что значение может содержать символы «=«. Все данные читаются в виде строк Юникода, поэтому дата, числа и логическое значение должны быть преобразованы из строкового представления в значения соответствующих типов.
Для преобразования даты используется функция бате!!ее.в!грт!ве() («в!г[пд рагве !1тпе« вЂ” парсинг строки со значением времени), которая принимает строку формата и возвращает объект аа!еттве. авте!!ве. Мы использовали строку формата, которая соответствует стандарту представления дат 130 8601, а затем для извлечения объекта типа аатет!ве. бате из полученного объекта Оз!ет!ве.
Оз!етгве использовали метод Озтет!ве. Озтет!ве. баТе(), так как нам требуется только дата, а не дата/время. Для преобразования числовых значений используются встроенные функции (1озт() и !пт(). Обратите внимание, что, например, вызов !и!("4.0") возбудит исключение Ча1аеЕггог. Поэтому, если необходимо более либеральное отношение при приеме целочисленных значений, можно использовать выражение !пт( 11азт("4.
О" ) ) или, если при этом необходимо выполнять округление, а не просто отсекать дробную часть, гоопэ((1оз!("4. О" ) ). Получить логическое значение немножко сложнее — например, вызов Ь001 ("О" ) вернет Тгае (непустая строка в логическом контексте имеет значение Тгае), поэтому сначала строку необходимо преобразовать в целое число. Ошибочные, отсутствующие или выходящие за допустимый диапазон значения всегда будут вызывать исключение. Если любое из преобразований потерпит неудачу, будет возбуждено исключение Ча1аеЕггаг. А если какое-либо из значений выйдет за допустимые пределы, будет возбуждено исключение 1пс!аеп!Еггог в тот момент, когда на основе прочитанных данных будет создаваться объект 1пс!Оепт. Запись и синтаксический анализ текстовых файлов 361 Если строка не содержит символ <=в, то проверяется — не является ли она маркером начала комментария.
В этом случае в переменную па ггаттче записывается пустая строка. Это означает, что при чтении всех последующих строк условное выражение в первой инструкции т( будет давать в результате значение Тгое, по меньшей мере, пока не будет прочитан маркер конца комментария. Если ни одно из условий в ветках 1( и е11Т не было выполнено, следовательно, возникла ошибка, поэтому в заключительном предложении е1эе возбуждается исключение КеуЕгго г, чтобы обозначить ее.
гетогп Тгое ехсерт (епч1гопвежеггог, ча1иееггог, кеуеггог, 1пс1сеп(Еггаг) аа егг: рг1пт("(О); 1врогт аггог: (1)".Тогват( оэ,рать.оаэепаве(эуэ.агдч[0]), егг)) гешгп Ра1эе Гтпа11у: тт ТП 1а пот Мола: то.с1аэе() Но окончании чтения всех строк вызывающей программе возвращается значение Тгое, если не было возбуждено исключение, — в этом случае блок ехсерт перехватит исключение, выведет для пользователя сообщение об ошибке и вернет Ра1эе. И в заключение, независимо от происходящего, файл будет закрыт.
Синтаксический анализ текста с помощью регулярных выражений Читателям, не знакомым с регулярными выражениями, рекомендуется прочитать главу 12, прежде чем приступать к чтению этого раздела, или сразу перейти к чтению следующего раздела (стр. 364) и вернуться сюда позднее. Использование регулярных выражений для разбора текста часто дает более короткий программный код по сравнению с тем, где все действия по разбору выполняются вручную, как это делалось в предыдущем подразделе, но в нем сложнее реализовать вывод ясных сообщений об ошибках. Ниже приводится программный код метода [врогт техт гедех(), который мы рассмотрим в два приема.
Сначала мы обсудим регулярные выражения, а затем реализацию синтаксического анализа, но опустим блоки ехсерт и Т1па1!у, поскольку в них не появилось ничего нового для нас. Оат тврогт тахт гадах(эе1(, Г(1епаве): )пс1септ ге = ге.совр!1е( г"'т[(?Р<)о>[ ]]+)т,](?Р<кеуча1оеа>.+?)" г" т.мАВВЯТ1УЕ ВТАВ?~.3(?Р<паггатзче>.*?)" г""'т. МЯВВЯТ!ЧЕ ЕМОт. 3", ге.ООТАЕ()ге.МО(Т1(1МЕ) Глава 7.
Работа с файлами Кеу ча1че ге = ге,совр!1е(г" 1е*(»Р<Кеу>[ =]»)Не*=1е*" г" (?Р<ча1ие>. »)1з*Г, ге. МНКТ1К1ИЕ) о Регулярные выражения записаны как «сырые» (гатч) «Сырые» стра,» строки. Это устраняет необходимость дублировать кажстр. 85 дый символ обратного олеша (вместо т записывать т,т,); например, если не использовать «сырые» строки, второе регулярное выражение пришлось бы записать как: " т та*(? Р<Кву>[ =]+) т,'тя*=ттв*(?Р<ча1ре>. »)тЛв*3". В этой книге для записи регулярных выражений мы всегда будем использовать «сырые» строки. Первое регулярное выражение, !пстбепт ге, используется для захвата всей записи с информацией об инциденте.
При таком подходе любой посторонний текст между записями останется незамеченным. Данное регулярное выражение в действительности состоит из двух частей. Первая часть \[(?Р<!б>[ ]]»)т](»Р<Кеуча1пез>,»с) соответствует символу «[», затем соответствует, с захватом в группу тб, произвольному числу символов, отличных от «]», затем соответствует символу «]» (что дает нам идентификатор отчета) и затем соответствует любому числу (но не менее одного) любых символов (включая символы перевода строки, благодаря флагу гв.
ОСТЯКАМ), захватывая их в группу Кеуча!- зев. Символы, включенные в группу Кеуча1рев, являются необходимым минимумом, чтобы перейти ко второй части регулярного выражения. Вторая часть первого регулярного выражения: т,.ИАННАТ1НЕ УТАИТ'т.3 (?Р<паггаттче>. *?) т. ИАННАТ1НЕ ЕИОт,. $. Она соответствУет точномУ тексту . ИАННЯТ1НЕ НТАНТ., затем произвольному числу символов, которые захватываются в группу паггат!че, и затем точному тексту .
ИАННЯТ1- НЕ ЕИО. в конце записи с информацией об инциденте. Флаг ге. МИ[11[!НЕ означает, что в данном регулярном выражении символ соответствует началу каждой строки в файле (а не началу всей строки), а символ 3 соответствует концу каждой строки в файле (а не концу всей строки), поэтому соответствие маркерам начала и конца комментария будет обнаруживаться, только если они находятся в начале строки.
Второе регулярное выражение Кеу ча1се ге используется для захвата строк ключ=значение и соответствует началу каждой строки в заданном тексте, произвольному (в том числе и нулевое) числу последующих пробельных символов, за которыми следуют символы, отличные от символа «=», захватываемые в группу Кеу, последующему символу «=» и всем остальным символам в строке текста (исключая начальные и завершающие пробельные символы), захватываемым в группу ча1пе. Основная логика синтаксического анализа файла осталась той же, что использовалась для анализа текста вручную и описана в предыдущем подразделе, только на этот раз сама запись и информация об инциденте извлекаются не посредством построчного чтения содержимого файла, а с помощью регулярных выражений.
Запись и синтаксический анализ текстовых файлов 363 гп = вопе Сгу: ГП = орел(ГС1епаве, епсоб1пд="отта") ае1т.с1еаг() Гог Спс1бепт ватсь 1п !псьбепт ге.т1пб11ег(ГП.геаб()): баСа = () ба1а[ "герогС 1б" ] = Спс1беп1 ваСса.дгочр("!б") баСа[ "паггаттче" ] = тех!итар. бебеп1( !по!берт васса. 9 гоар("паггастче" ) ) . осгтр() Кеуча1оеа = Спс1бепт васса.дгоор("Кеуча)чеа") гог васса 1п кеу ча1ое ге. гтпбтсег(кеуча1оеа); бата[загса.дгочр("Кеу")] = ватоа.дгоор("ча!че") ба1а[ "ба1е" ] = баСеттве. бате1аве. а1гр11ве( бата[ "ба1е" ], "вч-(Ып-вб").баСе() бата[ "рС1от регсепт Поога оп 1уре" ] = ( 11оат(баса[ "рпос регсепт поога оп Суре" ])) ба1а[ "р11о1 СоСа! Почга" ] = 1пт( баСа[" р11от Со1а! Поо га" ] ) бата[ "в1ба1г" ] = ооо1(1пс(бата[ "в!ба1г" ])) 11 1еп(бата) )= 9: га1ае Тпс(бепСЕггог("в1аа(пд баСа" ) !по!бел! = ТпоабепС(**бата) ее!1[ ьпс1бепс.герогт Сб] = Спс1беп1 ге1чгп Тгое Метод ге.
Г!пб!1ег() возвращает итератор, который поочередно возвращает неперекрывающиеся совпадения. В начале цикла создается словарь ба1а для хранения информации об инциденте, как и раньше, но на этот раз идентификатор отчета и текст комментария извлекаются непосредственно из найденного соответствия регулярному выражению !пс!беп1 ге. Затем из группы Кеуча)оеа извлекаются сразу все строки ключ=значение и к ним применяется метод ге, 11пб!Сег() регулярного выражения кеу ча]ое ге, чтобы выполнить обход отдельных строк ключ=значение. Каждая найденная пара (ключ, значение) помещается в словарь ба1а, поэтому все значения сохраняются в виде строк.