Skip to content

Home Администрирование Встроенные компоненты Python и модули str
Встроенные компоненты Python и модули str

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

Создание строк

Обычно строка создается путем заключения некоторого текста в кавычки:

Апострофы и кавычки, обычные и тройные, обозначают одно и то же: все они создают объект типа str. Апострофы и кавычки идентичны по своему действию и являются взаимозаменяемыми. Этим язык Python отличается от командных оболочек UNIX, где апострофы и кавычки не являются взаимозаменяемыми. Например:

В языке Perl апострофы и кавычки также не могут замещать друг друга при создании строк. Ниже приводится похожий пример на языке Perl.

И вот какие результаты дает этот небольшой сценарий на языке Perl:

Это различие отсутствует в языке Python. Право определять различия Python оставляет за программистом. Например, вы можете использовать апострофы, когда внутри строки должны находиться кавычки и вам не хотелось бы экранировать их (символом обратного слеша). Точно так же вы можете использовать кавычки, когда внутри строки должны присутствовать апострофы и вам не хотелось бы экранировать их, как показано в примере 3.1.

Пример 3.1. Кавычки и апострофы в языке Python

Обратите внимание, что во 2-й и 4-й строках вывода (Out [4] и Out [8]) включение в строку экранированных кавычек того же типа, что и окружающие строку, привело при выводе к изменению типа наружных кавычек. (В действительности это приводит отображение строки к «правильному» применению кавычек разных типов.)

Иногда бывает необходимо, чтобы в одной строке объединялось несколько строк. Иногда эту проблему можно решить, вставляя символ \п там, где необходимо создать разрыв строки, но это довольно неудобный способ. Другая, более ясная альтернатива заключается в использовании тройных кавычек, которые позволяют определять многострочный текст. В примере 3.2 демонстрируется неудачная попытка использовать апострофы для определения многострочного текста и успешная попытка использовать тройные апострофы.

Пример 3.2. Тройные кавычки

Помимо этого существует еще один способ обозначения строк, которые в языке Python называются «сырыми» строками. Сырые строки создаются добавлением символа г непосредственно перед открывающей кавычкой. По сути дела, сырые строки отличаются от обычных строк тем, что в сырых строках Python не интерпретирует экранированные последовательности символов, тогда как в обычных строках они интерпретируются. При интерпретации экранированных последовательностей в языке Python соблюдается практически тот же набор правил, который описывается стандартом языка С. Например, в обычных строках последовательность \t интерпретируется как символ табуляции, \п - как символ новой строки и \r - как перевод строки. В табл. 3.1 приводится список экранированных последовательностей в языке Python.

Таблица 3.1. Экранированные последовательности в языке Python

 

Об экранированных последовательностях и сырых строках стоит помнить, особенно, когда приходится иметь дело с регулярными выражениями, к которым мы подойдем далее В этом разделе. В примере 3.3 демонстрируется использование экранированных последовательностей и неформатированных строк.

Пример 3.3. Экранированные последовательности и сырые строки

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

Еще один фокус этого примера - различия между_ _repr_ _ и _ _str_ _ . Когда имя переменной вводится в строке приглашения оболочки IPython

и нажимается клавиша Enter, значение переменной отображается вы
зовом метода _ _repr_ _. Когда вводится инструкция print, которой пере
дается имя переменной, и нажимается клавиша Enter, переменная
отображается вызовом метода _ _str_ _. Инструкция print интерпрети
рует экранированные последовательности в строке и отображает их со
ответствующим образом.

Встроенные методы извлечения строковых данных

Строки в языке Python - это объекты, поэтому они имеют методы, которые могут вызываться для выполнения определенных операций. Однако под «методами» мы подразумеваем не только методы, которыми обладает тип str, но и любые другие способы, позволяющие извлекать данные из объектов типа str. Сюда входят все методы типа str, а также операторы in и not in, приведенные в примере, следующем ниже.

С технической точки зрения, операторы проверки условия in и not in вызывают метод _ _contains_ _() объекта str. За дополнительной информацией о том, как работают эти операторы, обращайтесь к приложению. Операторы in и not in могут использоваться для проверки, является ли некоторая строка частью другой строки, как показано в примере 3.4.

Пример 3.4. Операторы in и not in

Если строка string2 содержит строку string1, то выражение string1 in string2 вернет значение True, в противном случае - значение False. Поэтому проверка вхождения строки "Linux" в строку uname в нашем случае дает в результате значение True, а проверка вхождения строки "Darwin" в строку uname дает значение False. Применение оператора not in мы привели «для комплекта».

Иногда бывает достаточно узнать, что некоторая строка является подстрокой другой строки. А иногда требуется узнать, в какой позиции находится искомая подстрока. Выяснить это можно с помощью методов f ind() и index(), как показано в примере 3.5.

Пример 3.5. Методы find() и lndex()

Если строка stringl присутствует в строке string2 (как в данном примере), метод string2. find(string1) вернет индекс первого символа stringl в строке string2, в противном случае он вернет -1. (Не беспокойтесь, к индексам мы перейдем через мгновение). Точно так же, если строка stringl присутствует в строке string2, метод string2. index(string1) вернет индекс первого символа stringl в строке string2, в противном случае он возбудит исключение ValueError. В данном примере метод f ind() обнаружил подстроку "Linux" в начале строки, поэтому он вернул значение 0. Однако метод find() не смог обнаружить подстроку "Darwin" в этой строке, поэтому он вернул значение -1. Когда в операционной системе Linux была выполнена попытка отыскать подстроку "Linux" с помощью метода index(), был получен тот же результат, что и в случае применения метода find(). Но при попытке отыскать подстроку "Darwin" метод index() возбудил исключение ValueError, показывая, что не смог отыскать эту подстроку.

Итак, что можно делать с этими числовыми «индексами»? Зачем они нам нужны? Строки интерпретируются как списки символов. «Индекс», который возвращается методами find() и index(), просто показывает, начиная с какого символа в большей строке было обнаружено совпадение, как показано в примере 3.6.

Пример 3.6. Срез строки

Мы оказались в состоянии увидеть все символы, начиная с символа, индекс которого был получен в результате поиска подстроки "SMP", и до конца строки, воспользовавшись синтаксической конструкцией извлечения среза string[index:]. Мы также смогли увидеть все символы от начала строки uname до индекса, который был получен в результате поиска подстроки "SMP", применив синтаксическую конструкцию извлечения среза string[:index]. Все различия между этими двумя конструкциями заключаются в местоположении символа двоеточия (:) относительно индекса.

Цель примеров на извлечение среза строки и применения операторов in и not in состоит в том, чтобы показать вам, что строки являются последовательностями и поэтому обладают теми же особенностями, что и другие последовательности, такие как списки. Более полно последовательности обсуждаются в разделе «Sequence Operations» книги «Python in a Nutshell» (издательство O'Reilly) Алекса Мартелли (Alex Martelli) (этот раздел доступен в Интернете на сайте издательства: http://safari.oreilly.com/0596100469/pythonian-CHP-4-SECT-6).

Еще два строковых метода, startswith() и endswith(), как следует из их названий, помогут определить, «начинается» ли или «заканчивается» ли строка определенной подстрокой, как показано в примере 3.7.

Пример 3.7. Методы startswith() и endswithf )

Как видите, интерпретатор Python возвращает информацию, которая говорит о том, что строка «Raymond Luxury-Yacht» начинается с подстроки «Raymond» и заканчивается подстрокой «Luxury-Yacht». Она не начинается с подстроки «Throatwarbler» и не заканчивается подстрокой «Mangrove». Достаточно просто те же результаты можно получить

с помощью операции извлечения среза, но такой подход к решению выглядит менее наглядно и может показаться несколько утомительным в реализации, как показано в примере 3.8:

Пример 3.8. Имитация методов startswith() и endswlth()

Операция извлечения среза создает и возвращает новый строковый объект, а не изменяет саму строку. Если операции извлечения среза часто используются в сценарии, они могут оказывать существенное влияние на потребление памяти и на производительность. Даже если заметного влияния на производительность не ощущается, тем не менее, лучше воздержаться от использования операций извлечения среза в случаях, когда достаточно применения методов startswith() nendswithO.

Мы сумели убедиться, что первые символы в строке some_string, число которых равно длине строки «Raymond», соответствуют строке «Raymond». Другими словами, мы сумели убедиться, что строка some_string начинается с подстроки «Raymond», без использования метода startswith(). Точно так же мы смогли убедиться, что строка заканчивается подстрокой «Luxury-Yacht».

Методы lstrip(), rstrip() и strip() без аргументов удаляют ведущие, заключительные, и ведущие и заключительные пробельные символы, соответственно. В качестве таких пробельных символов можно назвать символы табуляции, пробелы, символы возврата каретки и новой строки. Метод lstrip() без аргументов удаляет любые пробельные символы, которые находятся в начале строки, и возвращает новую строку. Метод rst rip() без аргументов удаляет любые пробельные символы, которые находятся в конце строки, и возвращает новую строку. Метод st rip() без аргументов удаляет любые пробельные символы, которые находятся в начале и в конце строки, и возвращает новую строку, как показано в примере 3.9.

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

Пример 3.9. Методы lstrip(), rstrip() и strlp( )

Все три метода, lstrip(), rstrip() и strip(), могут принимать единственный необязательный аргумент: строку символов, которые следует удалить из соответствующего места строки. Это означает, что методы семейства strip() не просто удаляют пробельные символы - они могут удалять любые символы, какие вы укажете:

Здесь мы удалили из тега XML левую и правую угловые скобки, по одной за раз. А как быть, если нам потребуется удалить обе скобки одновременно? Сделать это можно следующим способом:

Методы семейства strip() возвращают строку, поэтому мы можем вызывать другие строковые операции прямо вслед за вызовом метода strip(). В этом примере мы объединили вызовы методов strip() в цепочку. Первый вызов метода strip() удаляет начальный символ (левую угловую скобку) и возвращает строку, а второй метод strip() удаляет завершающий символ (правую угловую скобку) и возвращает строку "some_tag". Однако существует более простой способ:

Возможно, вы подумали, что методы семейства strip() удаляют точное вхождение указанной подстроки, но в действительности удаляются любые последовательные вхождения указанных символов с соответствующей стороны строки. В этом последнем примере методу st rip() было предписано удалить "<>". Это не означает точное соответствие подстроке "о" и не означает, что должны быть удалены вхождения этих двух символов, следующих друг за другом именно в таком порядке, -это означает, что должны быть удалены символы "<" или ">", находящиеся в начале или в конце строки.

Ниже приводится, возможно, более понятный пример:

Здесь мы удалили все вхождения символов "<" или ">" с обоих концов строки. Таким способом мы можем ликвидировать простые символы и пробелы.

Следует заметить, что такой прием может работать несколько не так, как вы ожидаете, например:

У вас могло бы сложиться мнение, что метод strip() в этом примере удалит символы справа, но не слева. Однако он обнаружит и удалит любые последовательные вхождения символов "<", "f", "о" и ">". Это не ошибка, мы не пропустили второй символ "о". Вот еще один, заключительный пример использования метода strip(), который прояснит это утверждение:

Здесь удаляются символы ">", "<", "f", "о", хотя они следуют не в этом порядке.

Методы upper() и lower() удобно использовать, когда необходимо выполнить сравнение двух строк без учета регистра символов. Метод upper() возвращает строку со всеми символами из оригинальной строки в верхнем регистре. Метод lower() возвращает строку со всеми символами из оригинальной строки в нижнем регистре, как показано в примере 3.10.

Пример 3.10. Методы upper() и lowerf )

Если вам необходимо извлечь часть строки, ограниченной какими-либо символами-разделителями, метод split() предоставит вам эту возможность, как показано в примере 3.11.

Пример 3.11. Метод split()

Методу split() передается строка-разделитель, по которому необходимо разбить строку на подстроки. Часто это единственный символ, такой как запятая или вертикальная черта, но это может быть строка, содержащая более одного символа. В данном примере мы разбили

строку comma_delim_string по запятым, а строку pipe_delim_string - по символу вертикальной черты (|), передавая символ запятой или вертикальной черты методу split(). Возвращаемым значением метода является список строк, каждая из которых представляет собой группу последовательных символов, находящихся между разделителями. Когда в качестве разделителя необходимо использовать не единственный символ, а некоторую строку, метод split() справится и с этим. К моменту написания этих строк в языке Python отсутствовал символьный тип, поэтому хотя в примерах метод split() получал единственный символ, он рассматривался методом как строка. Поэтому, когда методу split() передается несколько символов, он обработает и их, как показано в примере 3.12.

Пример 3.12. Строка-разделитель в методе split()

Обратите внимание, что сначала мы использовали в качестве разделителя строку "XXX" для разделения строки multi_delim_string. Как и ожидалось, в результате был получен список ['pos1', 'pos2', 'pos3']. Затем, мы использовали в качестве разделителя строку XX" и метод split() вернул ['posl', 'Xpos2', 'ХрозЗ']. Здесь метод split() выбрал все символы, находящиеся между соседними разделителями "XX". Подстрока "posl" начинается с начала строки и простирается до первого разделителя "XX"; подстрока "Xpos2" располагается сразу за первым вхождением разделителя "XX" и простирается до второго его вхождения; и подстрока "ХроsЗ" располагается сразу за вторым вхождением XX" и простирается до конца строки. Последний вызов метода split() получает в качестве разделителя единственный символ "X". Обратите внимание, что позициям между соседними символами "X" соответствуют пустые строки ("") в результирующем списке. Это означает, что между соседними символами "X" ничего нет.

Но как быть, если необходимо разбить строку только по первым л вхождениям указанного разделителя? Для этого методу split() следует передать второй аргумент, с именем max_split. Когда во втором аргументе max_split методу split() передается целочисленное значение, он выполнит только указанное число разбиений исходной строки:

Здесь мы разбиваем строку по запятым и предписываем методу split() выполнить только одно разбиение. Несмотря на то, что в строке присутствует несколько запятых, строка была разбита только один раз.

Если необходимо разбить строку по пробелам, например, чтобы извлечь из текста отдельные слова, эту задачу легко можно решить вызовом метода split() без аргументов:

 

Когда метод split() не получает никаких аргументов, по умолчанию он выполняет разбиение строки по пробельным символам.

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

Для таких случаев лучше подходит метод splitlines():

Метод splitlines() возвращает список всех строк из многострочного текста и сохраняет группы «слов». После этого можно выполнить итерации по отдельным строкам текста и извлечь отдельные слова:

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

Учитывая, что исходные данные хранятся в виде списка, мы можем объединить строки 'one', 'two', 'three' и 'four' несколькими способами. Мы объединяем элементы списка some_list с помощью запятой, запятой и пробела, символа табуляции и пустой строки. Метод join() -это строковый метод, поэтому вызов его в качестве метода литерала, такого как ', ', является корректным. Метод join() принимает в качестве аргумента последовательность строк и объединяет их в одну строку так, чтобы элементы последовательности располагались в исходном порядке и отделялись строкой, для которой вызывается метод join().

Мы должны предупредить вас об особенностях поведения метода join() и об аргументе, который он ожидает получить. Обратите внимание: метод join() ожидает получить последовательность строк. А что произойдет, если ему передать последовательность целых чисел? Взгляните!

Или можно использовать выражение-генератор:

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

За дополнительной информацией об использовании генераторов списков обращайтесь к разделу «Control Flow Statements» в главе 4 книги «Python in a Nutshell» (этот раздел доступен в Интернете, на сайте издательства: http://safari.oreilly.com/0596100469/pythonian-CHP-4-SECT-10).

Последний метод, используемый для создания и изменения текстовых строк, - это метод replace(). Метод replace() принимает два аргумента: строку, которую требуется заменить, и строку замены, соответственно. Ниже приводится простой пример использования метода replace():

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

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

Так же, как операция извлечения среза и метод strip(), метод replace() не изменяет существующую строку, а создает новую.

Строки Юникода

До сих пор во всех примерах работы со строками, которые мы видели, использовались исключительно строковые объекты встроенного типа str, но в языке Python существует еще один строковый тип, с которым вам предстоит познакомиться: строки Юникода. Любые символы, которые выводятся на экран дисплея, внутри компьютера представлены числами. До появления кодировки Юникод существовало множество разнообразных наборов отображения числовых кодов в символы в зависимости от языка и платформы. Юникод - это стандарт, обеспечивающий единое отображение числовых кодов в символы, независимое от языка, платформы или даже программы, выполняющей обработку текста. В этом разделе мы рассмотрим понятие Юникода и способы работы с этой кодировкой, имеющиеся в языке Python. Подробное описание Юникода вы найдете в превосходном учебнике Э. М. Качлинга (А. М. Kuchling) по адресу: http://www.amk.ca/python/howto/unicode.

Или можно воспользоваться встроенной функцией unicode():

Создание строк Юникода выглядит ничуть не сложнее, чем создание обычных строк:

На первый взгляд, в этом нет ничего примечательного, особенно если учесть, что здесь мы имеем дело с символами одного языка. Но как быть, когда приходится работать с символами из нескольких языков? Здесь вам на помощь придет Юникод. Чтобы внутри строки Юникода создать символ с определенным числовым кодом, можно воспользоваться нотацией \uXXXX или \uXXXXXXXX. Например, ниже приводится строка Юникода, содержащая символы латиницы, греческого алфавита и кириллицы:

Интерпретатор генерирует строку (str) в зависимости от используемой кодировки. В версии Python, которая поставляется вместе с компьютерами Маc, если попытаться вывести строку из предыдущего примера с помощью инструкции print, будет получено сообщение об ошибке:

Мы должны определить другой кодек, который знает, как обрабатывать все символы в строке:

Здесь мы выполнили кодирование строки, содержащей символы латиницы, греческого алфавита и кириллицы, в кодировку UTF-8, которая наиболее часто используется для кодирования данных Юникода.

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

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

re

Раз поставка языка Python комплектуется в соответствии с принципом «батарейки включены», можно было бы ожидать, что в состав стандартной библиотеки будут включены модули для работы с регулярными выражениями. Так оно и есть. Акцент в этом разделе сделан на использовании в языке Python регулярных выражений, а не на подробностях их синтаксиса. Поэтому, если вы не знакомы с регулярными выражениями, рекомендуем вам приобрести книгу «Mastering Regular Expressions» (O'Reilly) Джеффри Е. Ф. Фридла (Jeffrey E. F. Friedl) (доступна также в Интернете на сайте издательства по адресу: http://safari.oreilly.com/0596528124).1 Далее мы предполагаем, что вы достаточно уверенно оперируете регулярными выражениями, в противном случае рекомендуем держать книгу Фридла под рукой.

Если вы знакомы с языком Perl, то, возможно, вы уже использовали регулярные выражения с оператором =". В языке Python поддержка регулярных выражений реализована на уровне библиотеки, а не на уровне синтаксических особенностей языка. Поэтому для работы с регулярными выражениями необходимо импортировать модуль re. Ни-

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

Пример 3.13. Простой пример использования регулярного выражения

Первое, что мы сделали в этом примере, - импортировали модуль re. Как вы уже наверняка поняли, имя re происходит от «regular expressions» (регулярные выражения). Затем мы создали строку re_string, которая будет играть роль шаблона для поиска. Этому шаблону будут соответствовать две открывающие фигурные скобки ({{), вслед за которыми может следовать текст, завершающийся двумя закрывающими фигурными скобками (}}). Затем мы создали строку some_string, которая содержит группы слов, окруженные фигурными скобками. И в конце мы выполнили обход результатов поиска в строке some_string по шаблону re_string, полученных от функции findall() из модуля ге. Как видите, пример вывел строки words, curly brackets, example и regular expressions, которые представляют все группы слов, заключенные в двойные фигурные скобки.

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

Итак, что же такое скомпилированное регулярное выражение? Это просто объект, созданный вызовом функции re.compile(), которой передается шаблон. Этот объект, созданный за счет передачи шаблона функции re.compile(), содержит множество методов для работы с регулярным выражением. Между скомпилированным и нескомпилированным регулярными выражениями имеются два основных отличия. Во-первых, вместо ссылки на шаблон регулярного выражения "{{.*?}}" создается объект скомпилированного выражения на основе шаблона. Во-вторых, вместо функции findall() из модуля re следует вызывать метод findall() объекта скомпилированного выражения.

За дополнительной информацией о всех функциях, имеющихся в модуле ге, обращайтесь к разделу «Module Contents» в справочнике «Python Library Reference», http://docs.python.org/lib/node46.html. За дополнительной информацией об объектах скомпилированных регулярных выражений обращайтесь к разделу «Regular Expression Objects» в справочнике «Python Library Reference», http://docs.python.org/lib/ re-objects.html.

В примере 3.14 представлена реализация предыдущего примера с двойными фигурными скобками, выполненная на основе использования объекта скомпилированного регулярного выражения.

Пример 3.14. Простое регулярное выражение, скомпилированный шаблон

Выбор метода работы с регулярными выражениями в языке Python зависит отчасти от личных предпочтений и от самого регулярного выражения. Следует заметить, что метод, основанный на использовании функций модуля re, уступает в производительности методу, основанному на использовании объектов скомпилированных регулярных выражений. Проблема производительности особенно остро может вставать, например, когда регулярное выражение применяется в цикле к каждой строке текстового файла, содержащего сотни и тысячи строк. В примерах ниже представлены реализации простых сценариев, использующих скомпилированые и нескомпилированные регулярные выражения, которые применяются к файлу, содержащему 500 000 строк текста. Если воспользоваться специальной функцией timeit, можно увидеть разницу в производительности между этими двумя сценариями. Смотрите пример 3.15.

Пример 3.15. Тест производительности нескомпилированного регулярного выражения

 

Функция timeit выполняет программный код несколько раз и возвращает время самого лучшего варианта. Ниже показаны результаты запуска утилиты timeit для этого сценария в оболочке IPython:

В этом примере функция run_ге() была вызвана 5 раз, и было вычислено среднее из 3 самых лучших показателей, которое составило 1,93 секунды. Специальная функция timeit выполняется с исследуемым программным кодом несколько раз, чтобы уменьшить погрешность, вызванную влиянием других процессов, исполняющихся в системе.

Ниже приводятся результаты измерения времени выполнения того же самого программного кода с помощью утилиты time операционной системы UNIX:

Пример 3.16 - это тот же пример с регулярным выражением за исключением того, что мы используем re.compile() для создания объекта скомпилированного шаблона.

Пример 3.16. Тест производительности скомпилированного регулярного выражения

Испытания с помощью специальной функции timeit в оболочке IРу-thon дали следующие результаты:

А испытания того же самого сценария с помощью утилиты time операционной системы UNIX дали следующие результаты:

Версия со скомпилированным регулярным выражением одержала чистую победу. Время работы этой версии оказалось в два раза меньше как по данным утилиты UNIX time, так и по данным функции timeit оболочки IPython. Поэтому мы настоятельно рекомендуем взять в привычку использовать объекты скомпилированных регулярных выражений.

Как уже говорилось ранее В этом разделе, для определения строк, в которых не интерпретируются экранированные последовательности, можно использовать сырые (неформатированные) строки. В примере 3.17 показано применение неформатированных строк для использования в регулярных выражениях.

Пример 3.17. Неформатированные строки и регулярные выражения

Шаблонный символ \b в регулярных выражениях соответствует границе слова. То есть, как в случае применения сырой строки, так и в случае применения обычной строки, мы предполагаем отыскать отдельные слова, состоящие из символов нижнего регистра. Обратите внимание, что при использовании raw_pattern были обнаружены отдельные слова в some_string, а при использовании non_raw_pattern вообще ничего не было найдено. В строке raw_pattern комбинация \b интерпретируется как два отдельных символа, в то время как в строке non_raw_pattern она интерпретируется как символ забоя (backspace). В результате функция f indall() сумела отыскать отдельные слова с помощью неформатированной строки шаблона. Однако при использовании шаблона в виде обычной строки функция findall() не отыскала ни одного символа забоя (backspace).

Чтобы с помощью шаблона non_raw_pattern можно было отыскать соответствие в строке, необходимо окружить требуемое слово символами \b, как показано ниже:

Обратите внимание на шестнадцатеричную форму записи символа "\х08" в соответствии, найденном функцией findall(). Эта шестнадца-теричная форма записи соответствует символам забоя (backspace), которые были добавлены с помощью экранированной последовательности \b.

Как видите, неформатированные строки могут пригодиться, когда предполагается использовать специальные последовательности, такие как "\b", обозначающую границу слова, "\d"» обозначающую цифру, или "\w", обозначающую алфавитно-цифровой символ. Полный перечень специальных последовательностей, начинающихся с символа обратного слеша, вы найдете в разделе «Regular Expression Syntax» в справочнике «Python Library Reference», http://docs.python.org/lib/ re-syntax.html.

Примеры с 3.14 по 3.17 были очень простыми. В них во всех использовались регулярные выражения и различные методы, применяемые к ним. Иногда такого ограниченного использования регулярных выражений вполне достаточно. Иногда бывает необходимо нечто более мощное, чем имеется в библиотеке регулярных выражений.

К основным методам (или функциям) регулярных выражений, которые используются наиболее часто, относятся findall(), finditer(), match() и search(). Вам также могут потребоваться методы split() и sub(), но, вероятно, не так часто, как другие методы.

Метод findall() отыскивает все вхождения указанного шаблона в строке. Если метод findall() найдет соответствия шаблону, тип возвращаемой структуры данных будет зависеть от наличия групп в шаблоне.

Краткое напоминание: группировка в регулярных выражениях позволяет указывать текст внутри регулярного выражения, который следует извлечь из результата. За дополнительной информацией обращайтесь к разделу «Common Metacharacters and Fields» в книге Фридла (Friedl) «Mastering Regular Expressions» или в Интернете по адресу: http://safari.oreilly.com/ 0596528124/regex3-CHP-3-SECT-5?imagepage=137.

Если в регулярном выражении отсутствуют группы, а совпадение найдено, тогда findall() вернет список строк. Например:

В этом шаблоне отсутствуют группы, поэтому findall() возвращает список строк. Здесь можно наблюдать интересный побочный эффект -последний элемент списка содержит два слова, tint и tire. Используемое здесь регулярное выражение соответствует словам, начинающимся с символа «t» и заканчивающимся символом «е». Но часть выражения . *? соответствует любым символам, включая пробелы. Метод findall() отыскал все, что предполагалось. Он отыскал слово, начинающееся с символа «t» (tint), и продолжил просмотр строки, пока не обнаружил слово, завершающееся символом «е» (tire). Поэтому соответствие «tint tire» вполне согласуется с шаблоном. Чтобы исключить пробел, можно было бы использовать регулярное выражение r'\bt\w*е\b':

Второй тип структуры данных, который может быть получен, - это список кортежей. Если группы присутствуют в выражении и было найдено совпадение, то findall() вернет список кортежей. Подобный шаблон и строка показаны в примере 3.18.

Пример 3.18. Простая группировка и метод findallf )

Несмотря на свою простоту, этот пример демонстрирует ряд важных моментов. Во-первых, обратите внимание, что этот простой шаблон нелепо длинен и содержит массу неалфавитно-цифровых символов, от которых начинает рябить в глазах, если смотреть слишком долго. Это обычная вещь для многих регулярных выражений. Затем, обратите внимание, что шаблон содержит явные вложенные группы. Объемлющая группа будет соответствовать любому тексту, начинающемуся с символа «А» и заканчивающемуся точкой. Символы между начальным символом «А» и завершающей точкой образуют вложенные группы, которые должны соответствовать словам «big» или «small», «brown» или «purple» и так далее. Далее, возвращаемое значение метода findall() представляет собой список кортежей. Элементами этих кортежей являются группы, которые были определены в регулярном выражении. Первый элемент кортежа - все предложение, потому что оно соответствует наибольшей, объемлющей группе. Последующие элементы кортежа соответствуют каждой из подгрупп. Наконец, обратите внимание на последний аргумент в вызове метода re.compile() -re. VERBOSE. Это позволило нам записать регулярное выражение в многострочном режиме, то есть мы смогли расположить регулярное выражение в нескольких строках, не оказывая влияния на поиск соответствий. Пробел, оказавшийся за пределами группировки, был проигнори-

рован. Хотя мы и не продемонстрировали здесь такую возможность, тем не менее, многострочный режим позволяет вставлять комментарии в конец каждой строки регулярного выражения, чтобы описать, что делает та или иная его часть. Одна из основных сложностей, связанных с регулярными выражениями, состоит в том, что описание шаблона часто бывает очень длинным и трудным для чтения. Цель re. VERBOSE состоит в том, чтобы упростить написание регулярных выражений, следовательно, это ценный инструмент, облегчающий сопровождение программного кода, содержащего регулярные выражения.

Метод finditerO является разновидностью метода findall(). Вместо того чтобы возвращать список кортежей, как это делает метод find-all(), finditer() возвращает итератор, как это следует из имени метода. Каждый элемент итератора - это объект найденного совпадения, который мы обсудим далее В этом разделе. Пример 3.19 реализует тот же простой пример, только в нем вместо метода findall() используется метод finditer().

Пример 3.19. Пример использования метода finditerf )

Если прежде вы никогда не сталкивались с итераторами, вы можете представлять их себе как списки, которые создаются в тот момент, когда они необходимы. Один из недостатков такого определения состоит в том, что вы не можете обратиться к определенному элементу итератора по его индексу, как, например, к элементу списка some_list[3]. Вследствие этого ограничения вы не можете получить срез итератора, как, например, в случае списка some_list[2:6]. Тем не менее, независимо от этих ограничений итераторы представляют собой легкое и мощное средство, особенно когда необходимо выполнить итерации через некоторую последовательность, потому что при этом последовательность не загружается целиком в память, а элементы ее возвращаются по требованию. Это позволяет итераторам занимать меньший объем

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

Еще одно преимущество метода finditer() перед findall() состоит в том, что каждый элемент, возвращаемый методом finditer(), - это объект match, а не простой список строк или список кортежей, соответствующих найденному тексту.

Методы match() и search() обеспечивают похожие функциональные возможности. Оба метода применяют регулярное выражение к строке; оба указывают, с какой позиции начать и в какой закончить поиск по шаблону; оба возвращают объект match для первого найденного соответствия заданному шаблону. Разница между этими двумя методами состоит в том, что метод match() пытается отыскать совпадение только от начала строки или от указанного места в строке, не переходя в другие позиции в строке, а метод search() будет пытаться отыскать соответствие шаблону в любом месте строки или между начальной и конечной позицией, которые вы укажете, как показано в примере 3.20.

Пример 3.20. Сравнение методов match() и search()

Даже при том, что в строке search_string имеется соответствие шаблону, поиск по которому производит метод match(), тем не менее, поиск завершается неудачей, потому что подстрока в search_string, соответствующая шаблону, находится не в начале строки. Метод search(), напротив, нашел соответствие и вернул объект match.

Методы search() и match() принимают параметры, определяющие начальную и конечную позицию поиска в строке, как показано в примере 3.21.

Пример 3.21. Параметры начала и конца поиска в методах search( ) и match()

Параметр pos - это индекс, определяющий место в строке, откуда должен начинаться поиск по шаблону. В данном примере передача параметра роз методу search() не повлияла на результат, но передача параметра роз методу match()привела к тому, что он нашел соответствие шаблону, хотя без параметра роз соответствие обнаружить не удалось. Установка параметра endpos в значение 3 привела к тому, что оба метода - и match(), и search() не нашли соответствие, потому что соответствие шаблону включает символ в третьей позиции.

Методы findall() и finditer() отвечают на вопрос: «чему соответствует мой шаблон?», а главный вопрос, на который отвечают методы search() и match(): «имеется ли соответствие моему шаблону?». Методы search() и match() отвечают также на вопрос: «каково первое соответствие моему шаблону?», но часто единственное, что требуется узнать, это: «имеется ли соответствие моему шаблону?». Например, предположим, что необходимо написать сценарий, который должен читать строки из файла журнала и обертывать каждую строку в теги HTML, чтобы обеспечить удобочитаемое отображение. При этом хотелось бы, чтобы все строки, содержащие текст «ERROR», отображались красным цветом, для чего можно было бы выполнить цикл по всем строкам в файле, проверить их с помощью регулярного выражения и, если метод search() обнаруживает текст «ERROR», можно было бы определить такой формат строки, чтобы она отображалась красным цветом.

Методы search() и match() удобны не только тем, что они определяют наличие соответствия, но и тем, что они возвращают объект match. Объекты match содержат различные методы извлечения данных, которые могут пригодиться при обходе полученных результатов. Особый интерес представляют такие методы объекта match, как start(), end(), span(), groups() и groupdict().

Методы start(), end() и span() определяют позиции в строке поиска, где совпадение с шаблоном начинается и где заканчивается. Метод start() возвращает целое число, определяющее позицию в строке начала найденного соответствия. Метод end() возвращает целое число, определяющее позицию в строке конца найденного соответствия. А метод span() возвращает кортеж, содержащий позицию начала и конца совпадения.

Метод groups() возвращает кортеж совпадения, каждый элемент которого соответствует группе, имеющейся в шаблоне. Этот кортеж напоминает кортежи в списке, возвращаемом методом findall(). Метод groupdict() возвращает словарь именованных групп, ключи которого соответствуют именам групп, присутствующих непосредственно в регулярном выражении, например: (?P<group_name>pattern).

Подводя итоги, можно сказать - чтобы эффективно использовать регулярные выражения, следует взять в привычку использовать объекты скомпилированных регулярных выражений. Используйте методы findall() и finditer(), когда необходимо получить части текста, соответствующие шаблону. Запомните, что метод finditer() обладает более высокой гибкостью, чем findall(), потому что возвращает итератор по объектам match. Более подробный обзор библиотеки регулярных выражений вы найдете в главе 9 книги «Python in a Nutshell» Алекса Мар-телли (Alex Martelli) (O'Reilly). Чтобы познакомиться с регулярными выражениями в действии, обращайтесь к книге «Data Crunching» Гре-га Уилсона (Greg Wilson) (The Pragmatic Bookshelf).

Работа с конфигурационным файлом Apache

Теперь, когда вы получили представление о работе с регулярными выражениями в языке Python, попробуем поработать с конфигурационным файлом веб-сервера Apache:

Это слегка измененный конфигурационный файл Apache в Ubuntu. Мы создали именованные виртуальные хосты для некоторых своих нужд. Мы также добавили в файл /etc/hosts следующую строку:

127.0.0.1        1оса12

Она позволяет указать броузеру, что серверу с именем 1оса12 соответствует IP-адрес 127.0.01, то есть локальный компьютер. И в чем же здесь смысл? Если в броузере ввести адрес http://local2, он передаст серверу

указанное имя в заголовке HTTP. Ниже приводится HTTP-запрос, направленный серверу 1оса12:

Обратите внимание, что запрос начинается с заголовка Host:. Когда веб-сервер Apache получит такой запрос, он направит его виртуальному хосту с именем 1оса12.

Теперь все, что нам предстоит сделать, - это написать сценарий, который анализирует конфигурационный файл веб-сервера Apache, такой, как показано выше, отыскивает раздел VirtualHost и замещает значение параметра DocumentRoot в этом разделе. Сам сценарий приводится ниже:

Этот сценарий сначала создает три объекта скомпилированных регулярных выражений: один соответствует открывающему тегу Virtual-Host, один - закрывающему тегу VirtualHost и один - строке с параметром DocumentRoot. Мы также создали функцию, которая выполняет эту утомительную работу. Функция называется replace_docroot и принимает в качестве аргументов тело конфигурационного файла в виде строки, имя раздела VirtualHost, который требуется отыскать, и значение параметра DocumentRoot, которое требуется назначить для данного виртуального хоста. Функция устанавливает признак состояния, который указывает, находится ли текущая анализируемая строка в разделе VirtualHost. Кроме того, сохраняется имя текущего виртуального хоста. При анализе строк в разделе VirtualHost эта функция пытается отыскать строку с параметром DocumentRoot и изменяет его значение. Поскольку функция replace_docroot() выполняет итерации по каждой строке в конфигурационном файле, она возвращает либо неизмененную исходную строку, либо измененную строку с параметром DocumentRoot.

Мы создали простой интерфейс командной строки к этой функции. В нем не предусматривается использование ничего особенного, такого как функция optparse, и не выполняется проверка на количество входных аргументов, но он работает. Теперь попробуем применить этот сценарий к конфигурационному файлу веб-сервера Apache, представленному выше, и изменим настройки VirtualHost 1оса12:80 так, чтобы он использовал каталог /imp в качестве корневого каталога документов. Предусмотренный нами интерфейс командной строки просто выводит строки, возвращаемые функцией replace_docroot(), а не изменяет сам файл:

Единственная строка, которая изменилась, - это строка с параметром DocumentRoot в разделе VirtualHost 1оса12:80. Ниже приводятся различия, полученные после того, как вывод сценария был перенаправлен в файл:

Изменение значения параметра DocumentRoot в конфигурационном файле веб-сервера Apache — это достаточно простая задача, но когда это приходится делать достаточно часто или когда имеется множество виртуальных хостов, которые приходится изменять, тогда есть смысл написать сценарий, подобный тому, что был показан выше. Не менее просто можно было бы изменить сценарий так, чтобы он комментировал требуемый раздел VirtualHost, изменял значение параметра LogLevel или изменял имя файла журнала для указанного виртуального хоста.

Работа с файлами

Овладение приемами работы с файлами является ключом к обработке текстовых данных. Зачастую текст, который требуется обработать, находится в текстовом файле, например, в файле журнала, в конфигурационном файле или в файле с данными приложения. Нередко результаты анализа данных требуется сохранить в виде файла отчета или просто записать их в текстовый файл для последующего изучения. К счастью, в языке Python имеется простой в использовании тип объектов с именем file, который в состоянии помочь выполнить все необходимые действия с файлами.

Создание файлов

Это может показаться странным, но чтобы прочитать содержимое существующего файла, необходимо создать новый объект типа file. Однако не надо путать операцию создания нового объекта с созданием нового файла. Чтобы выполнить операцию записи в файл, необходимо создать новый объект file и, возможно, создать новый файл на диске, поэтому в такой ситуации создание объекта file интуитивно более понятно, чем создание объекта file для чтения. Создавать объект file обязательно, потому что он необходим для организации взаимодействий с файлом на диске.

Для создания объекта file используется встроенная функция open(). Ниже приводится фрагмент программного кода, который открывает файл для чтения:

Функция open() - это встроенная функция, поэтому нет никакой необходимости импортировать какой-либо модуль. Функция open() принимает три аргумента: имя файла, режим открытия и размер буфера. Обязательным является только первый аргумент - имя файла. Наиболее часто используются режимы: «r» (режим чтения, используется по умолчанию), «w» (режим записи) и «а» (режим записи в конец файла). Вместе с этими тремя спецификаторами режимов может использоваться дополнительный спецификатор «b», определяющий двоичный режим доступа. Третий аргумент, размер буфера, определяет способ буферизации операций над файлом.

В предыдущем примере было предписано открыть файл foo.txt в режиме для чтения и сохранить ссылку на созданный объект файла в переменной infile. После получения ссылки на объект в переменной infile появилась возможность обратиться к методу read() этого объекта, который читает содержимое файла целиком.

В этом примере предписывается открыть файл foo_out.txt в режиме для записи и сохранить ссылку на вновь созданный объект типа file

Создание объекта типа file для записи в файл выполняется почти так же, как создание объекта для чтения из файла. Просто вместо спецификатора режима "r" следует использовать спецификатор "w":

в переменной outputfile. После получения ссылки на объект мы смогли обратиться к методу write(), чтобы записать в файл некоторый текст и закрыть его вызовом метода close().

Несмотря на всю простоту создания файлов, у вас может появиться желание создавать файлы способом, более устойчивым к появлению ошибок. Считается хорошей практикой обертывать вызов функции open() конструкцией try/finally, особенно, когда вслед за этим вызывается метод write(). Ниже приводится пример реализации записи в файл с использованием инструкции try/finally:

При такой реализации записи файлов метод close() вызывается, когда где-нибудь в блоке try возникает исключение. В действительности этот подход позволяет методу close() закрыть файл, даже когда в блоке try не возникает исключение. Блок finally выполняется после завершения работы блока try всегда, независимо от того, возникло исключение или нет.

В версии Python 2.5 появилась новая идиома - инструкция with, ко
торая позволяет использовать менеджер контекста. Менеджер
контекста - это просто объект с методами _ _enter _ _ () и_ exit_ _(). Ко
гда объект создается с помощью инструкции with, вызывается метод
_ _enter_ _() менеджера контекста. Когда выполнение блока with завер
шается, вызывается метод _ _exit_ _() менеджера контекста, даже если

возникло исключение. Объекты типа file имеют методы _ _enter_ _() и

_ exit _ _(). В методе _ _exit _ _() объекта типа file вызывается метод

Хотя в этом фрагменте отсутствует вызов метода close() объекта f, менеджер контекста закроет файл после выхода из блока with:

close(). Ниже приводится пример использования инструкции with:

Как и следовало ожидать, файл был закрыт. Хотя это хорошая практика - обрабатывать все возможные исключения и гарантировать закрытие файла, когда это необходимо, но ради простоты и ясности мы не будем предусматривать такую обработку во всех примерах.

Полный перечень методов объектов типа file вы найдете в разделе «File Objects» в справочнике «Python Library Reference» по адресу: http:// docs.python.org/lib/bltin-f ile-objects.html.

Чтение из файлов

Как только появляется объект файла, открытого для чтения с флагом r, вы получаете возможность использовать три обычных метода объекта file, удобных для получения данных, содержащихся в файле: read(), readline() и readlines(). Метод read() читает, что неудивительно, данные из объекта открытого файла и возвращает эти данные в виде строки. Метод read() принимает необязательный аргумент, который указывает число байтов, которые требуется прочитать из файла. Если этот аргумент отсутствует, метод read() попытается прочитать содержимое файла целиком. Если размер файла меньше, чем величина аргумента, метод read() будет читать данные, пока не встретит конец файла и вернет то, что удалось прочитать.

Тогда метод read() будет работать с этим файлом, как показано ниже:

Допустим, что имеется следующий файл:

Обратите внимание, что символы новой строки отображаются как последовательности \п - это стандартный способ обозначения символа новой строки.

Следующий метод, позволяющий получать текст из файла, - метод readline(). Метод readline() читает текст из файла по одной строке за

Если бы требовалось прочитать только первые 5 байтов, сделать это можно было бы следующим способом:

раз. Этот метод принимает один необязательный аргумент: size. Он определяет максимальное число байтов, которые метод readlineQ будет читать из файла, прежде чем вернуть строку, независимо от того, был достигнут конец строки или нет. Поэтому в следующем примере программа читает первую строку из текста из файла foo.txt, затем читает первые 7 байтов текста из второй строки, а после этого считывает остаток второй строки:

Последний метод получения текста из объектов типа file, который мы рассмотрим, - это метод readlines(). Имя readlines() - это не опечатка и не ошибка, закравшаяся при копировании имени метода из предыдущего примера. Метод readlines() читает сразу все строки из файла. Впрочем, это почти правда. Метод readlines() имеет аргумент sizehint, определяющий максимальное число байтов, которые требуется прочитать. В следующем примере мы создали файл biglines.txt, содержащий 10 000 строк, в каждой из которых по 80 символов. После этого мы открыли файл, указали, что нам требуется прочитать из файла N первых строк, общий объем которых составляет примерно 1024 байта, определили число прочитанных строк и байтов и затем прочитали оставшуюся часть файла:

Команда в строке [3] показывает, что было прочитано 102 строки, а команда в строке [4] показала, что общее число прочитанных байтов составило 8262. Как так вышло, что мы указали «примерное» число байтов, которые требуется прочитать, равное 1024, а получили 8262? Оно

было округлено до размера внутреннего буфера, который равен примерно 8 килобайтам. Суть в том, что аргумент sizehint не всегда оказывает влияние так, как вам того хотелось бы, и об этом следует помнить.

Запись в файлы

Иногда возникает потребность не только читать данные из файлов, но также создавать собственные файлы и записывать в них данные. Объекты типа file обладают двумя основными методами, которые позволят вам записывать данные в файлы. Первый метод, который уже демонстрировался выше, - это метод write(). Метод write() принимает один аргумент: строку, которую требуется записать в файл. В следующем примере демонстрируется запись данных в файл:

Команда [1] открывает файл с флагом режима w, то есть в режиме для записи. Команда [2] записывает в файл две строки. Команда [4] создает новый объект файла и присваивает ссылку на него другой переменной, с именем д, чтобы избежать путаницы, хотя вполне возможно было использовать и переменную f. И команда [5] показывает, что метод read() возвращает те же самые данные, которые были записаны в файл.

И еще один пример функции-генератора, которая может использоваться для записи данных в файл (этот пример функционально эквива-

Следующий основной метод записи данных в файл - это метод write-lines(). Метод writelines() принимает один обязательный параметр: последовательность, которая должна быть записана в файл. Допускается использовать последовательности любого итерируемого типа, такие как списки, кортежи, генераторы списков (которые можно считать списками) или генераторы. Ниже приводится пример вызова метода writelines(), который получает данные для записи в файл от выражения-генератора:

лентен предыдущему, но для его реализации потребовалось написать больше программного кода):

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

Дополнительные ресурсы

За дополнительной информацией об объектах типа file обращайтесь к главе 7 в книге «Learning Python» Дэвида Ашера (David Ascher) и Марка Лутца (Mark Lutz) (O'Reilly) (имеется также в Интернете по адресу: http://safari.oreilly.com/0596002815/lpython2-chp-7-sect-2) или к разделу «File Objects» в справочнике «Python Library Reference» (доступному в Интернете по адресу: http://docs.python.org/lib/bltin-fi-le-objects.html).

За дополнительной информацией о выражениях-генераторах обращайтесь к разделу «generator expressions» в справочнике «Python Language Reference» (доступному в Интернете по адресу: http:// docs.python.org/ref/genexpr.html). За дополнительной информацией об инструкции yield обращайтесь к разделу «yield statement» в справочнике «Python Language Reference» (доступному в Интернете по адресу: http://docs.python.org/ref/yield.html).

Стандартный ввод и вывод

Операции чтения текста из потока стандартного ввода процесса и записи в поток стандартного вывода процесса знакомы большинству сис-

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

Стандартная библиотека языка Python содержит встроенный модуль с именем sys, который обеспечивает простые способы доступа к стандартному вводу и стандартному выводу. Стандартная библиотека предоставляет доступ к стандартному вводу и выводу как к объектам типа file, несмотря на то, что они не имеют никакого отношения к файлам на диске. А так как эти объекты напоминают объекты типа file, для работы с ними можно использовать те же самые методы, которые используются при работе с файлами. Вы можете работать с ними, как если бы это были файлы на диске, и обращаться к соответствующим методам для выполнения требуемых операций.

После импортирования модуля sys стандартный ввод становится доступен в виде атрибута stdin этого модуля (sys.stdin). Атрибут sys. stdin -это доступный для чтения объект типа file. Обратите внимание, что произойдет, если создать «настоящий» объект типа file, открыв файл с именем foo.txt на диске, и затем сравнить его с объектом sys. stdin:

Интерпретатор воспринимает их как объекты одного и того же типа, поэтому они обладают одними и теми же методами. Несмотря на то, что с технической точки зрения эти объекты принадлежат одному и тому же типу и обладают одними и теми же методами, поведение некоторые методов будет отличаться. Например, методы sys.stdin.seek() и sys. stdin. tell() доступны, но при обращении к ним возбуждается исключение (в данном случае исключение IOError). Однако во всем остальном стандартный ввод и вывод напоминают объекты типа file, и вы в значительной степени можете воспринимать их как обычные дисковые файлы.

Доступ к sys. stdin в оболочке Python (или в оболочке IPython) практически лишен всякого смысла. Попытка импортировать модуль sys и вызвать метод sys. stdin. read() просто заблокирует работу оболочки. Чтобы продемонстрировать вам, как работает объект sys. stdin, мы

написали сценарий, который читает данные из sys.stdin и выводит обратно каждую прочитанную строку с соответствующим ей номером, как показано в примере 3.22.

Пример 3.22. Нумерация строк, читаемых методом sys.stdin.readline

В этом примере мы создали переменную counter, с помощью которой сценарий следит за номерами введенных строк. Далее следует цикл while, в теле которого выполняется чтение строк со стандартного ввода. Для каждой строки вводится ее порядковый номер и ее содержимое. Так как программа все время находится в процессе выполнения цикла, она обрабатывает все строки, которые ей поступают, даже если они оказываются пустыми. Но даже пустые строки - не совсем пустые: они содержат символ новой строки (\п). Когда сценарий обнаруживает признак «конца файла», он прерывает работу цикла.

Ниже приводится результат объединения в конвейер команды who и предыдущего сценария:

Достаточно интересно, что предыдущий пример можно реализовать гораздо проще и короче, если использовать функцию enumerate(), как показано в примере 3.23.

Пример 3.23. Пример использования метода sys.stdin.readline( )

Чтобы получить доступ к стандартному вводу, необходимо импортировать модуль sys и затем воспользоваться атрибутом stdin. Точно так же, чтобы получить доступ к стандартному выводу, необходимо импортировать модуль sys и воспользоваться атрибутом stdout. Так же, как sys. stdin представляет объект файла, доступного для чтения, объект sys. stdout представляет объект файла, доступного для записи. И так же, как sys.stdin имеет тот же тип, что и объект файла, доступного для чтения, объект sys.stdout имеет тот же тип, что и объект файла, доступного для записи:

Важное замечание: следующее утверждение не должно быть неожиданным, поскольку любой файл, открытый для чтения, и любой файл, открытый для записи, относятся к одному и тому же типу:

Важно знать, что sys.stdout может в значительной степени рассматриваться как объект типа file, открытый для записи, точно так же как и sys.stdin может рассматриваться как объект типа file, открытый для чтения.

StringlO

Как быть в случае, когда функция, выполняющая обработку текста, предназначена для работы с объектом типа file, а данные, которые предстоит обрабатывать, доступны в виде текстовой строки, а не в виде объекта file? Самое простое решение состоит в том, чтобы воспользоваться модулем StringIO:

В этом примере мы создали объект StringlO, конструктору которого передается строка "This is a\nmultiline string. \nreadline() should see\nmul-tiple lines of\ninput". После этого появилась возможность вызывать метод readline() объекта StringlO. Хотя в этом примере использовался только метод readline(), это далеко не единственный доступный метод, заимствованный у объектов типа file:

Конечно, между файлами и строками существуют различия, но интерфейс позволяет легко переходить между использованием файлов и строк. Ниже приводится сравнение методов и атрибутов объектов типа file и объектов типа StringlO:

Как видите, если возникнет необходимость работать со строкой как с файлом, объект типа StringlO может оказать существенную помощь.

urllib

Что, если интересующий вас файл находится где-то в сети? Или вы хотите использовать уже существующий фрагмент программного кода, который предполагает работу с объектом file? Встроенный тип file не умеет взаимодействовать с сетью, и в этой ситуации вам поможет модуль urllib.

Если вам требуется только вызвать метод read() для получения данных файла, расположенного на некотором веб-сервере, для этого можно просто воспользоваться методом urllib.urlopen(), как показано в простом примере ниже:

Здесь сначала импортируется модуль urllib. Затем создается file-подобный объект с именем url_f lie. Далее выполняется чтение содержимого url_file в строку с именем urllib_docs. И только для того, чтобы

продемонстрировать, что данные действительно были получены из Интернета, с помощью операции извлечения среза выводятся первые и последние 80 символов полученного документа. Обратите внимание, что объекты файлов, созданные средствами urllib, поддерживают методы read() и close(). Кроме того, они поддерживают методы read-line(), readlines(), fileno(), info() и geturl().

Если вам потребуются более широкие возможности, такие как работа через прокси-сервер, ищите дополнительную информацию о модуле urllib по адресу: http://docs.python.org/lib/module-urllib.html. Если вам требуются еще более широкие возможности, такие как аутентификация и работа с cookie, подумайте об использовании модуля urllib2, описание которого вы найдете по адресу: http://docs.python.org/lib/ module-urllib2.html.

{linkr:related}

Комментарии (0)

RSS feed Comments

Написать комментарий

smaller | bigger

busy
 

Регистрация




Top