Kodomo

Пользователь

Учебная страница курса биоинформатики,
год поступления 2012

Файлы и Строки

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

В питоне (как и во многих языках программирования), основная идея работы с файлом такая:

Мы будем рассматривать, как работать в питоне с текстовыми файлами (например, файлы .txt, исходники питонских программ, html, fasta, genbank). Работа с бинарными файлами (например, вордовские файлы, или картинки) принципиально ничем не отличается от текстовых, однако требует гораздо более сложного знания устройства формата файла. Как правило, для работы с бинарными файлами проще всего найти готовую библиотку, которая будет представлять его содержимое удобным для нас образом в питоне. Таких библиотек для питона создано множество для разных форматов файлов, однако работа с ними стоит за рамками основного курса (многие из них сопровождаются хорошей документацией с примерами).

Открытие файла.

Соответственно, прежде, чем с файлом начинать работать, нам нужно создать объект, в котором будет храниться курсор в файле. Делается это так:

   1 file = open('file.txt')

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

   1 file = open('file.txt', "w")

Тут сразу предупреждаю: в тот момент, когда питон открывает файл на запись, если такой файл уже существовал, то он будет стёрт! Это свойство даже не столько питона, сколько большинства операционных систем. Его нужно всегда учитывать: например, нельзя указывать одинаковым входной и выходной файл для программы.

Что делать, если мы хотим дописать в файл? Для этого существуют еще один режим работы файла 'a' (от 'append'):

   1 file = open('file.txt', "a") 

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

   1 f = open("hello.txt") # открыть файл hello.txt в текущей директории
   2 
   3 # UNIX only:
   4 f = open("../task2/README") # открыть файл README, директорией выше в директории task2
   5 f = open("_darcs/prefs/author") # открыть файл author в поддиректории prefs в директории _darcs
   6 f = open("/bin/ls") # открыть файл ls в директории bin, лежащей в корневой директории /
   7 
   8 # Windows only:
   9 f = open("..\\Task2\\README")
  10 f = open("_darcs\\prefs\\author")
  11 f = open("H:\\Task3\\Python\\ls.py")

Так эти строки будут выглядять изнутри питона. Если имена файлов приходят из аргументов командной строки, то добавлять лишние бэкслеши не надо. (В командной строке будет H:\Task3\Python – то, как FAR пишет пути, если ему щёлкнуть Ctrl-Enter по файлу).

Дабы не писать лишние бэкслеши, в питоне есть ещё один тип строк, о котором мы вам не рассказывали: если перед строкой стоит буква r, то тогда бэкслеши в теле строки ничего не значат для самого питона:

   1 f = open(r"..\Task2\README")

Чтение по строкам

Если мы подставляем файл туда, где питон ожидал бы список, то питон будет интерпретировать файл как список строк файла – включая переносы в концах строк.

   1 for line in file:
   2         print line

Если мы распечатаем каждую строку таким образом, то в итоге на экране мы увидим все строки через пробел (пустую строку). Это произойдет из-за того, что в файле в конце каждой строки стоит символ '\n', а print автоматически прибавляет этот символ к концу печатаемого объекта. О решении этой проблемы поговорим чуть позже (см. Работа со строками и всякие полезные вещи при работе с файлами).

Чтение по байтам.

   1 bytes=file.read(1)

метод .read(1) возвращает строку bytes, в которой будет находиться 1 байт, прочитанный из файла.

Чтение целиком

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

   1 contents=file.read()

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

   1 file.write("Hello, file!\n")

дописывает одну строку в конец файла (если мы не применяем усилий, курсор в файле, в который мы пишем, всегда оказывается в конце).

Обратите внимания: в отличие от print, эта функция переноса строки в конец не дописывает, нам приходится вставлять его туда самостоятельно!

Как закрывать файл

Чтобы закрыть файл, нужно на объекте файла вызвать метод close:

   1 file = open('hello.txt', 'w')
   2 file.write("Hello, world!\n")
   3 file.close()

Однако, в питоне есть более удобный способ, чтобы питон сам закрыл за нами файл. Для этого в питоне есть конструкция with: питон закроет файл в тот момент, когда мы из неё выходим (то есть возвращаем старый отступ):

   1 with open('hello.txt', 'w') as file:
   2     file.write("Hello, world!\n")

Стандартные потоки ввода-вывода

Существует три специальных потока, которые называются: стандартный поток ввода, стандартный поток вывода и стандартный поток ошибок.

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

В питоне каждый из этих потоков выглядит как обычный файл. Имена этим файлам: sys.stdin, sys.stdout, sys.stderr. (Разумеется, чтобы ими пользоваться, нужно сначала проимпортировать модуль sys).

Работа со строками.

Итак, вернемся к чтению по строкам.

   1 for line in file:
   2         print line

Как уже было сказано, у нас возникает проблема с удвоением '\n'. Избавиться от этого лишнего пробельного символа можно с помощью метода strip().

.strip(): обрезание символов с концов строки

strip отрезает с начала и конца строки те символы, которые указаны в его аргументах (т.е. в скобках). Пустые скобки означают пробельные символы

   1 >>> a=" \t 1 2 "
   2 >>> a.strip()
   3 '1 2'

Важно! Строки нельзя изменить, поэтому все методы редактирования строк просто возвращают новую отредактированную строку, а старую не меняют.

То есть:

   1 >>> a=" \t 1 2 "
   2 >>> a.strip()
   3 '1 2'
   4 >>> a
   5 " \t 1 2 "

Если мы напишем:

   1 >>> a=a.strip()
   2 >>> a
   3 '1 2'

то a будет указывать теперь на новую строку (изменит адрес), однако исходная строка останется прежней.

Еще примеры со strip:

   1 >>> '123abc231'.strip('12')
   2 '3abc23'

Как уже было сказано, strip обрезает с начала и конца строки все символы, которые указаны в его аргументах. Он работает только посимвольно! Поэтому с его помощью нельзя вырезать из строки '123abc321' число '12', чтобы осталось '3abc3'.

Вернемся к нашей проблеме. Метод strip() можно использовать, однако он удалит еще пробелы и в начале, что уже не есть хорошо. Чтобы избежать этого, надо указать в аргументах символ '\n', который обычно присутствует только в конце. Для полной уверенности можно использовать разновидность метода strip – rstrip, который вырезает символы с правого конца (аналогично, существует метод lstrip для левого). Тогда:

   1 for line in file:
   2     print line.rstrip('\n')

.split(): разбиение строки по заданным разделителям

Разбить строку на элементы списка можно с помощью метода .split()

   1 >>> " \t 1 2 ".split()
   2 ['1', '2']  

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

   1 >>> '1,_2,_3,4,_5'.split(',_') # в аргументе split указываем разделитель на элементы
   2 ['1', '2', '3,4', '5']
   3 >>> a='1,_2,_3,4,_5'.split(',')
   4 ['1', '_2', '_3', '4', '_5']

split("строка") разбивает по строке, а не по символам, из которых она состоит! (Не путайте со strip)

split() при разбиении выкидывает пробелы в начале и в конце строки (смотри пример выше), а split(" ") нет!

   1 >>> " \t 1 2 ".split(' ')
   2 ['', '\t', '1', '2', '']

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

split можно попросить искать в строке разделитель не более, чем n раз: s.split(separator, n). Нетрудно догадаться, что такой вызов вернёт список длины не более, чем n+1. Например:

   1 >>> ',,1,2,,3'.split(',', 2)
   2 ['', '', '1,2,,3']
   3 >>> '1'.split(',', 2)
   4 ['1']

Поэтому существует и двойственный метод rsplit, который делает то же самое, что и split, но если ему дают второй аргумент, то он ищет не более, чем n последних разделителей.

.join(): операция, обратная к split:

   1 >>> line = 'a\t b \tc\t'
   2 >>> fields = line.split("\t")
   3 >>> fields
   4 ['a', ' b ', 'c', '']
   5 >>> "\t".join(fields)
   6 'a\t b \tc\t'

Несколько странным может показаться, что join нужно вызывать как метод строки, которой мы объединяем элементы списка, а не как метод списка, который мы склеиваем. Этому есть псевдоразумные заумные объяснения, с которыми некоторые соглашаются, некоторые нет. Оставим это на совести Гвидо, и просто запомним, что с join нужно не путать, как его вызывать.

.replace()

Замена всех вхождений подстроки в строке на другую подстроку. Особых комментариев не требует.

   1 >>> "abcbd".replace("b", "x")
   2 "axcxd"
   3 >>> "abcbd".replace("b", "")
   4 "acd"

.lower(), .upper(): приведение строки к одному регистру

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

   1 >>> 'Ku-Ku'.lower()
   2 'ku-ku'
   3 >>> 'Ku-Ku'.upper()
   4 'KU-KU'
   5 >>> 'ku-KU'.capitalize()
   6 'Ku-ku'

.find(): поиск подстроки в строке

Возвращает индекс первого вхождения подстроки в строку:

   1 >>> 'I like limes'.find('li')
   2 2

Можно также искать только в заданной подстроке исходной строки:

   1 >>> 'I like limes'.find('li', 4) # ищем начиная с буквы с номером 4 (то есть пятой буквы)
   2 7
   3 >>> 'I like limes'[4:].find('li') # ищем начиная с буквы с номером 4 (то есть пятой буквы) -- но если мы делаем это своими руками, нужно не забыть прибавить обратно к номеру позиции находки откушенную нами часть строки
   4 3

Если ничего не нашлось, возвращается -1:

   1 >>> 'I like limes'.find('li', 4, 7)
   2 -1

startswith

Возвращает True, если строка начинается с данной подстроки:

   1 >>> 'I like limes'.startswith('I like')
   2 True

Так же, как и с find(), можно ограничивать подстроку в исходной строке:

   1 >>> 'I like limes'.startswith('like', 2)
   2 True

Слайсы: string[start:end]

Со строками в питоне можно работать так же, как и со списками:

   1 >>> "abcbd"[1:-1]
   2 'bcb'
   3 >>> len("abcbd")
   4 5
   5 >>> "abcbd"[1::2]
   6 "bb"
   7 >>> "abcbd"[1]
   8 "b"

Только вот элементами этого списка являются снова строки (хотя некоторым людям казалось бы, что строка – это список всё-таки из букв). Если задуматься, это довольно странная идея (например, x[0][0][0][0] говорит нам либо о том, что x – это очень сложная конструкция из вложенных друг в друга списков, либо строка, из которой мы просто очень извращённым способом берём первую букву), однако, она довольно популярна, и используется во многих относительно молодых языках программирования (например, javascript).

Параметры командной строки

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

Например, вспомним команду ls. Ее можно вызвать с различными параметрами.

Все три записи выше равнозначны между собой.

Если параметр написан через знак дефиса, то он называется именованным, иначе – позиционным.

В переменной sys.argv содержится список строк – все то, что было написано в командной строке (разделенное по пробелам).

Например, если мы напишем маленькую программку test_argv.py:

   1 import sys
   2 print sys.argv

И запустим ее с какими-нибудь параметрами, то получится так:

$ python test_argv.py file.txt -r -w 5
['test_argv.py', 'file.txt', '-r', '-w', '5']