Программирование на языке Ruby.
10.1. Файлы и каталоги.
Под файлом мы обычно, хотя и не всегда, понимаем файл на диске. Концепция файла в Ruby, как и в других языках, — это полезная абстракция. Говоря «каталог», мы подразумеваем каталог или папку в смысле, принятом в UNIX и Windows.
Класс File тесно связан с классом IO, которому наследует. Класс Dir связан с ним не так тесно, но мы решили рассмотреть файлы и каталоги вместе, поскольку между ними имеется концептуальная связь.
10.1.1. Открытие и закрытие файлов.
Метод класса File.new, создающий новый объект File, также открывает файл. Первым параметром, естественно, является имя файла.
Необязательный второй параметр называется строкой указания режимам он говорит, как нужно открывать файл — для чтения, для записи и т.д. (Строка указания режима не имеет ничего общего с разрешениями.) По умолчанию предполагается режим "r", то есть только чтение. Ниже показано, как открывать файлы для чтения и записи.
File1 = File.new("one") # Открыть для чтения.
File2 = File.new("two", "w") # Открыть для записи.
Есть также разновидность метода new, принимающая три параметра. В этом случае второй параметр задает начальные разрешения для файла (обычно записывается в виде восьмеричной константы), а третий представляет собой набор флагов, объединенных союзом ИЛИ. Флаги обозначаются константами, например: File::CREAT (создать файл, если он еще не существует) и File::RDONLY (открыть только для чтения). Такая форма используется редко.
File = File.new("three", 0755, File::CREAT|File::WRONLY).
В виде любезности по отношению к операционной системе и среде исполнения всегда закрывайте открытые вами файлы. Если файл был открыт для записи, то это не просто вежливость, а способ предотвратить потерю данных. Для закрытия файла предназначен метод close:
Out = File.new("captains.log", "w").
# Обработка файла...
Out.close.
Имеется также метод open. В простейшей форме это синоним new:
Trans = File.open("transactions","w").
Но методу open можно также передать блок, и это более интересно. Если блок задан, то ему в качестве параметра передается открытый файл. Файл остается открытым на протяжении всего времени нахождения в блоке и автоматически закрывается при выходе из него. Пример:
File.open("somefile","w") do |file|
file.puts "Строка 1".
file.puts "Строка 2".
file.puts "Третья и последняя строка".
End.
# Теперь файл закрыт.
Это изящный способ обеспечить закрытие файла по завершении работы с ним. К тому же при такой записи весь код обработки файла сосредоточен в одном месте.
10.1.2. Обновление файла.
Чтобы открыть файл для чтения и записи, достаточно добавить знак плюс ( +) в строку указания режима (см. раздел 10.1.1):
F1 = File.new("file1", "r+").
# Чтение/запись, от начала файла.
F2 = File.new("file2", "w+").
# Чтение/запись; усечь существующий файл или создать новый.
F3 = File.new("file3", "а+").
# Чтение/запись; перейти в конец существующего файла или создать новый.
10.1.3. Дописывание в конец файла.
Чтобы дописать данные в конец существующего файла, нужно задать строку указания режима "а" (см. раздел 10.1.1):
Logfile = File.open("captains_log", "a").
# Добавить строку в конец и закрыть файл.
Logfile.puts "Stardate 47824.1: Our show has been canceled.".
Logfile.close.
10.1.4. Прямой доступ к файлу.
Для чтения из файла в произвольном порядке, а не последовательно, можно воспользоваться методом seek, который класс File наследует от IO. Проще всего перейти на байт в указанной позиции. Номер позиции отсчитывается от начала файла, причем самый первый байт находится в позиции 0.
# myfile содержит строку: abcdefghi.
File = File.new("myfile").
File.seek(5).
Str = file.gets # "fghi".
Если все строки в файле имеют одинаковую длину, то можно перейти сразу в начало нужной строки:
# Предполагается, что все строки имеют длину 20.
# Строка N начинается с байта (N-1)*20.
File = File.new("fixedlines").
File.seek(5*20) # Шестая строка!
Для выполнения относительного поиска воспользуйтесь вторым параметром. Константа IO::SEEK_CUR означает, что смещение задано относительно текущей позиции (и может быть отрицательным):
File = File.new("somefile").
File.seek(55) # Позиция 55.
File.seek(-22, IO::SEEK_CUR) # Позиция 33.
File.seek(47, IO::SEEK_CUR) # Позиция 80.
Можно также искать относительно конца файла, в таком случае смещение может быть только отрицательным:
File.seek(-20, IO::SEEK_END) # Двадцать байтов от конца файла.
Есть еще и третья константа IO::SEEK_SET, но это значение по умолчанию (поиск относительно начала файла).
Метод tell возвращает текущее значение позиции в файле, у него есть синоним pos:
File.seek(20).
Pos1 = file.tell # 20.
File.seek(50, IO::SEEK_CUR).
Pos2 = file.pos # 70.
Метод rewind устанавливает указатель файла в начало. Его название («обратная перемотка») восходит ко временам использования магнитных лент.
Для выполнения прямого доступа файл часто открывается в режиме обновления (для чтения и записи). Этот режим обозначается знаком + в начале строки указания режима (см. раздел 10.1.2).
10.1.5. Работа с двоичными файлами.
Когда-то давно программисты на языке С включали в строку указания режима символ "b" для открытия файла как двоичного. (Вопреки распространенному заблуждению, это относилось и к ранним версиям UNIX.) Как правило, эту возможность все еще поддерживают ради совместимости, но сегодня с двоичными файлами работать не так сложно, как раньше. Строка в Ruby может содержать двоичные данные, а для чтения двоичного файла не нужно никаких специальных действий.
Исключение составляет семейство операционных систем Windows, в которых различие все еще имеет место. Основное отличие двоичных файлов от текстовых на этой платформе состоит в том, что в двоичном режиме конец строки не преобразуется в один символ перевода строки, а представляется в виде пары «возврат каретки — перевод строки». Еще одно важное отличие — интерпретация символа control-Z как конца файла в текстовом режиме:
# Создать файл (в двоичном режиме).
File.open("myfile","wb") {|f| f.syswrite("12345\0326789\r") }.
#Обратите внимание на восьмеричное 032 (^Z).
# Читать как двоичный файл.
Str = nil.
File.open("myfile","rb") {|f| str = f.sysread(15) ).
Puts str.size # 11.
# Читать как текстовый файл.
Str = nil.
File.open("myfile","r") {|f| str = f.sysread(15) }.
Puts str.size # 5.
В следующем фрагменте показано, что на платформе Windows символ возврата каретки не преобразуется в двоичном режиме:
# Входной файл содержит всего одну строку: Строка 1.
File = File.open("data").
Line = file.readline # "Строка 1.\n".
Puts "#{line.size} символов." # 10 символов,
File.close.
File = File.open("data","rb").
Line = file.readline # "Строка 1.\r\n".
Puts "#{line.size} символов." # 11 символов.
File.close.
Отметим, что упомянутый в коде метод binmode переключает поток в двоичный режим. После переключения вернуться в текстовый режим невозможно.
File = File.open("data").
File.binmode.
Line = file.readline # "Строка 1.\r\n".
Puts {line.size} символов." # 11 символов.
File.close.
При необходимости выполнить низкоуровневый ввод/вывод можете воспользоваться методами sysread и syswrite. Первый принимает в качестве параметра число подлежащих чтению байтов, второй принимает строку и возвращает число записанных байтов. (Если вы начали читать из потока методом sysread, то никакие другие методы использовать не следует. Результаты могут быть непредсказуемы.).
Input = File.new("infile").
Output = File.new("outfile").
Instr = input.sysread(10);
Bytes = output.syswrite("Это тест.").
Отметим, что метод sysread возбуждает исключение EOFError при попытке вызвать его, когда достигнут конец файла (но не в том случае, когда конец файла встретился в ходе успешной операции чтения). Оба метода возбуждают исключение SystemCallError при возникновении ошибки ввода/вывода.
При работе с двоичными данными могут оказаться полезны метод pack из класса Array и метод unpack из класса String.
10.1.6. Блокировка файлов.
В тех операционных системах, которые поддерживают такую возможность, метод flock класса File блокирует или разблокирует файл. Вторым параметром может быть одна из констант File::LOCK_EX, File::LOCK_NB, File::LOCK_SH, File::LOCK_UN или их объединение с помощью оператора ИЛИ. Понятно, что многие комбинации не имеют смысла; чаще всего употребляется флаг, задающий неблокирующий режим.
File = File.new("somefile").
File.flock(File::LOCK_EX) # Исключительная блокировка; никакой другой.
# процесс не может обратиться к файлу.
File.flock(File::LOCK_UN) # Разблокировать.
File.flock(File::LOCK_SH) # Разделяемая блокировка (другие.
# процессы могут сделать то же самое).
File.flock(File::LOCK_UN) # Разблокировать.
Locked = file.flock(File::LOCK_EX | File::LOCK_NB).
# Пытаемся заблокировать файл, но не приостанавливаем программу, если.
# не получилось; в таком случае переменная locked будет равна false.
Для семейства операционных систем Windows эта функция не реализована.
10.1.7. Простой ввод/вывод.
Вы уже знакомы с некоторыми методами ввода/вывода из модуля Kernel; мы вызывали их без указания вызывающего объекта. К ним относятся функции gets и puts, а также print, printf и p (последний вызывает метод объекта inspect, чтобы распечатать его в понятном для нас виде).
Но есть и другие методы, которые следует упомянуть для полноты. Метод putc выводит один символ. (Парный метод getc не реализован в модуле Kernel по техническим причинам, однако он есть у любого объекта класса IO). Если параметром является объект String, то печатается первый символ строки.
Putc(?\n) # Вывести символ новой строки.
Putc("X") # Вывести букву X.
Интересный вопрос: куда направляется вывод, если эти методы вызываются без указания объекта? Начнем с того, что в среде исполнения Ruby определены три глобальные константы, соответствующие трем стандартным потокам ввода/вывода, к которым мы привыкли в UNIX. Это STDIN, STDOUT и STDERR. Все они имеют тип IO.
Имеется также глобальная переменная $stdout, именно в нее направляется весь вывод, формируемый методами из Kernel. Она инициализирована значением STDOUT, так что данные отправляются на стандартный вывод, как и следовало ожидать. В любой момент переменной $stdout можно присвоить другое значение, являющееся объектом IO.
Diskfile = File.new("foofile","w").
Puts "Привет..." # Выводится на stdout.
$stdout = diskfile.
Puts "Пока!" # Выводится в файл "foofile".
Diskfile.close.
$stdout = STDOUT # Восстановление исходного значения.
Puts "Это все." # Выводится на stdout.
Помимо метода gets в модуле Kernel есть методы ввода readline и readlines. Первый аналогичен gets в том смысле, что возбуждает исключение EOFError при попытке читать за концом файла, а не просто возвращает nil. Последний эквивалентен методу IO.readlines (то есть считывает весь файл в память).
Откуда мы получаем ввод? Есть переменная $stdin, которая по умолчанию равна STDIN. Точно так же существует поток стандартного вывода для ошибок ( $stderr, по умолчанию равен STDERR).
Еще имеется интересный глобальный объект ARGF, представляющий конкатенацию всех файлов, указанных в командной строке. Это не объект класса File, хотя и напоминает таковой. По умолчанию ввод связан именно с этим объектом, если в командной строке задан хотя бы один файл.
# Прочитать все файлы, а затем вывести их.
Puts ARGF.read.
# А при таком способе более экономно расходуется память:
While ! ARGF.eof?
puts ARGF.readline.
End.
# Пример: ruby cat.rb file1 file2 file3.
При чтении из стандартного ввода ( stdin) методы Kernel не вызываются. Потому можно обойти (или не обходить) ARGF, как показано ниже:
# Прочитать строку из стандартного ввода.
Str1 = STDIN.gets.
# Прочитать строку из ARGF.
Str2 = ARGF.gets.
# А теперь снова из стандартного ввода.
Str3 = STDIN.gets.
10.1.8. Буферизованный и небуферизованный ввод/вывод.
В некоторых случаях Ruby осуществляет буферизацию самостоятельно. Рассмотрим следующий фрагмент:
Print "Привет... ".
Sleep 10.
Print "Пока!\n".
Если запустить эту программу, то вы увидите, что сообщения «Привет» и «Пока» появляются одновременно, после завершения sleep. При этом первое сообщение не завершается символом новой строки.
Это можно исправить, вызвав метод flush для опустошения буфера вывода. В данном случае вывод идет в поток $defout (подразумеваемый по умолчанию для всех методов Kernel, которые занимаются выводом). И поведение оказывается ожидаемым, то есть первое сообщение появляется раньше второго.
Print "Привет... ".
STDOUT.flush.
Sleep 10.
Print "Пока!\n".
Буферизацию можно отключить (или включить) методом sync=, а метод sync позволяет узнать текущее состояние.
Buf_flag = $defout.sync # true.
STDOUT.sync = false.
Buf_flag = STDOUT.sync # false.
Есть еще по крайней мере один низкий уровень буферизации, который не виден. Если метод getc возвращает символ и продвигает вперед указатель файла или потока, то метод ungetc возвращает символ назад в поток.
Ch = mystream.getc # ?А.
Mystream.ungetc(?C).
Ch = mystream.getc # ?C.
Тут следует иметь в виду три вещи. Во-первых, только что упомянутая буферизация не имеет отношения к механизму буферизации, о котором мы говорили выше в этом разделе. Иными словами, предложение sync=false не отключает ее. Во-вторых, вернуть в поток можно только один символ; при попытке вызвать метод ungetc несколько раз будет возвращен только символ, прочитанный последним. И, в-третьих, метод ungetc не работает для принципиально небуферизуемых операций (например, sysread).
10.1.9. Манипулирование правами владения и разрешениями на доступ к файлу.
Вопрос о владении файлами и разрешениях сильно зависит от платформы. Как правило, в системе UNIX функций больше, чем предоставляет Ruby, а на других платформах многие возможности не реализованы.
Для определения владельца и группы файла (это целые числа) класс File::Stat предоставляет методы экземпляра uid и gid:
Data = File.stat("somefile").
Owner_id = data.uid.
Group_id = data.gid.
В классе File::Stat есть также метод экземпляра mode, который возвращает текущий набор разрешений для файла.
Perms = File.stat("somefile").mode.
В классе File имеется метод класса и экземпляра chown, позволяющий изменить идентификаторы владельца и группы. Метод класса принимает произвольное число файлов. Если идентификатор не нужно изменять, можно передать nil или -1.
Uid = 201.
Gid = 10.
File.chown(uid, gid, "alpha", "beta").
F1 = File.new("delta").
F1.chown(uid, gid).
F2 = File.new("gamma").
F2.chown(nil, gid) # Оставить идентификатор владельца без изменения.
Разрешения можно изменить с помощью метода chmod (у него также есть два варианта: метод класса и метод экземпляра). Традиционно разрешения представляют восьмеричным числом, хотя это и не обязательно.
File.chmod(0644, "epsilon", "theta").
F = File.new("eta").
F.chmod(0444).
Процесс всегда работает от имени какого-то пользователя (возможно, root), поэтому с ним связан идентификатор пользователя (мы сейчас говорим о действующем идентификаторе). Часто нужно знать, имеет ли данный пользователь право читать, писать или исполнять данный файл. В классе File::Stat есть методы экземпляра для получения такой информации.
Info = File.stat("/tmp/secrets").
Rflag = info.readable?
Wflag = info.writable?
Xflag = info.executable?
Иногда нужно отличить действующий идентификатор пользователя от реального. На этот случай предлагаются методы экземпляра readable_real?, writable_real? и executable_real?.
Info = File.stat("/tmp/secrets").
Rflag2 = info.readable_real?
Wflag2 = info.writable_real?
Xflag2 = info.executable_real?
Можно сравнить владельца файла с действующим идентификатором пользователя (и идентификатором группы) текущего процесса. В классе File::Stat для этого есть методы owned? и grpowned?.
Отметим, что многие из этих методов можно найти также в модуле FileTest:
Rflag = FileTest::readable?("pentagon_files").
# Прочие методы: writable? executable? readable_real?
# writable_real? executable_real? owned? grpowned?
# Отсутствуют здесь: uid gid mode.
Маска umask, ассоциированная с процессом, определяет начальные разрешения для всех созданных им файлов. Стандартные разрешения 0777 логически пересекаются (AND) с отрицанием umask, то есть биты, поднятые в маске, «маскируются» или сбрасываются. Если вам удобнее, можете представлять себе эту операцию как вычитание (без занимания). Следовательно, если задана маска 022, то все файлы создаются с разрешениями 0755.
Получить или установить маску можно с помощью метода umask класса File. Если ему передан параметр, то он становится новым значением маски (при этом метод возвращает старое значение).
File.umask(0237) # Установить umask.
Current_umask = File.umask # 0237.
Некоторые биты режима файла (например, бит фиксации — sticky bit) не имеют прямого отношения к разрешениям. Эта тема обсуждается в разделе 10.1.12.
10.1.10. Получение и установка временных штампов.
С каждым файлом на диске связано несколько временных штампов (в разных операционных системах они различны). Ruby понимает три таких штампа: время модификации (когда в последний раз было изменено содержимое файла), время доступа (когда в последний раз файл читался) и время изменения (когда в последний раз была изменена информация о файле, хранящаяся в каталоге).
Получить эту информацию можно тремя разными способами, хотя все они дают один и тот же результат.
Методы mtime, atime и ctime класса File возвращают временные штампы, не требуя предварительного открытия файла или даже создания объекта File.
T1 = File.mtime("somefile").
# Thu Jan 04 09:03:10 GMT-6:00 2001.
T2 = File.atime("somefile").
# Tue Jan 09 10:03:34 GMT-6:00 2001.
T3 = File.ctime("somefile").
# Sun Nov 26 23:48:32 GMT-6:00 2000.
Если файл, представленный экземпляром File, уже открыт, то можно воспользоваться методами этого экземпляра.
Myfile = File.new("somefile").
T1 = myfile.mtime.
T2 = myfile.atime.
T3 = myfile.ctime.
А если имеется экземпляр класса File::Stat, то и у него есть методы, позволяющие получить ту же информацию:
Myfile = File.new("somefile").
Info = myfile.stat.
T1 = info.mtime.
T2 = info.atime.
T3 = info.ctime.
Отметим, что объект File::Stat возвращается методом класса (или экземпляра) stat из класса File. Метод класса lstat (или одноименный метод экземпляра) делает то же самое, но возвращает информацию о состоянии самой ссылки, а не файла, на который она ведет. Если имеется цепочка из нескольких ссылок, то метод следует по ней и возвращает информацию о предпоследней (которая уже указывает на настоящий файл).
Для изменения времени доступа и модификации применяется метод utime, которому можно передать несколько файлов. Время можно создать в виде объекта Time или числа секунд, прошедших с точки отсчета.
Today = Time.now.
Yesterday = today - 86400.
File.utime(today, today, "alpha").
File.utime(today, yesterday, "beta", "gamma").
Поскольку оба временных штампа изменяются одновременно, то при желании оставить один без изменения его сначала следует получить и сохранить.
Mtime = File.mtime("delta").
File.utime(Time.now, mtime, "delta").
10.1.11. Проверка существования и получение размера файла.
Часто необходимо знать, существует ли файл с данным именем. Это позволяет выяснить метод exist? из модуля FileTest:
Flag = FileTest::exist?("LochNessMonster").
Flag = FileTest::exists?("UFO").
# exists? является синонимом exist?
Понятно, что такой метод не может быть методом экземпляра File, поскольку после создания объекта файл уже открыт. В классе File мог бы быть метод класса с именем exist?, но его там нет.
С вопросом о том, существует ли файл, связан другой вопрос: а есть ли в нем какие-нибудь данные? Ведь файл может существовать, но иметь нулевую длину — а это практически равносильно тому, что он отсутствует.
Если нас интересует только, пуст ли файл, то в классе File::Stat есть два метода экземпляра, отвечающих на этот вопрос. Метод zero? возвращает true, если длина файла равна нулю, и false в противном случае.
Flag = File.new("somefile").stat.zero?
Метод size? возвращает либо размер файла в байтах, если он больше нуля, либо nil для файла нулевой длины. Не сразу понятно, почему nil, а не 0. Дело в том, что метод предполагалось использовать в качестве предиката, а значение истинности нуля в Ruby — true, тогда как для nil оно равно false.
If File.new("myfile").stat.size?
puts "В файле есть данные.".
Else.
puts "Файл пуст.".
End.
Методы zero? и size? включены также в модуль FileTest:
Flag1 = FileTest::zero?("file1").
Flag2 = FileTest::size?("file2").
Далее возникает следующий вопрос: «Каков размер файла?» Мы уже видели что для непустого файла метод size? возвращает длину. Но если мы применяем его не в качестве предиката, то значение nil только путает.
В классе File есть метод класса (но не метод экземпляра) для ответа на этот вопрос. Метод экземпляра с таким же именем имеется в классе File::Stat.
Size1 = File.size("file1").
Size2 = File.stat("file2").size.
Чтобы получить размер файла в блоках, а не в байтах, можно обратиться к методу blocks из класса File::Stat. Результат, конечно, зависит от операционной системы. (Метод blksize сообщает размер блока операционной системы.).
Info = File.stat("somefile").
Total_bytes = info.blocks * info.blksize.
10.1.12. Опрос специальных свойств файла.
У файла есть много свойств, которые можно опросить. Мы перечислим в этом разделе те встроенные методы, для которых не нашлось другого места. Почти все они являются предикатами.
Читая этот раздел (да и большую часть этой главы), помните о двух вещах. Во-первых, так как класс File подмешивает модуль FileTest, то любую проверку, для которой требуется вызывать метод, квалифицированный именем модуля, можно также выполнить, обратившись к методу экземпляра любого файлового объекта. Во-вторых, функциональность модуля FileTest и объекта File::Stat (возвращаемого методом stat или lstat) сильно перекрывается. В некоторых случаях есть целых три разных способа вызвать по сути один и тот же метод. Мы не будем каждый раз приводить все варианты.
В некоторых операционных системах устройства подразделяются на блочные и символьные. Файл может ссылаться как на то, так и на другое, но не на оба сразу. Методы blockdev? и chardev? из модуля FileTest проверяют тип устройства:
Flag1 = FileTest::chardev?("/dev/hdisk0") # false.
Flag2 = FileTest::blockdev?("/dev/hdisk0") # true.
Иногда нужно знать, ассоциирован ли данный поток с терминалом. Метод tty? класса IO (синоним isatty) дает ответ на этот вопрос:
Flag1 = STDIN.tty? # true.
Flag2 = File.new("diskfile").isatty # false.
Поток может быть связан с каналом (pipe) или сокетом. В модуле FileTest есть методы для опроса этих условий:
Flag1 = FileTest::pipe?(myfile).
Flag2 = FileTest::socket?(myfile).
Напомним, что каталог — это разновидность файла. Поэтому нужно уметь отличать каталоги от обычных файлов, для чего предназначены два метода из модуля FileTest:
File1 = File.new("/tmp").
File2 = File.new("/tmp/myfile").
Test1 = file1.directory? # true.
Test2 = file1.file? # false.
Test3 = file2.directory? # false.
Test4 = file2.file? # true.
В классе File есть также метод класса ftype, который сообщает вид потока; одноименный метод экземпляра находится в классе File::Stat. Этот метод возвращает одну из следующих строк: file, directory, blockSpecial, characterSpecial, fifo, link или socket (строка fifо относится к каналу).
This_kind = File.ftype("/dev/hdisk0") # "blockSpecial".
That_kind = File.new("/tmp").stat.ftype # "directory".
В маске, описывающей режим файла, можно устанавливать или сбрасывать некоторые биты. Они не имеют прямого отношения к битам, обсуждавшимся в разделе 10.1.9. Речь идет о битах set-group-id, set-user-id и бите фиксации (sticky bit). Для каждого из них есть метод в модуле FileTest.
File = File.new("somefile").
Info = file.stat.
Sticky_flag = info.sticky?
Setgid_flag = info.setgid?
Setuid_flag = info.setuid?
К дисковому файлу могут вести символические или физические ссылки (в тех операционных системах, где такой механизм поддерживается). Чтобы проверить, является ли файл символической ссылкой на другой файл, обратитесь к методу symlink? из модуля FileTest. Для подсчета числа физических ссылок на файл служит метод nlink (он есть только в классе File::Stat). Физическая ссылка неотличима от обычного файла — это просто файл, для которого есть несколько имен и записей в каталоге.
File.symlink("yourfile","myfile") # Создать ссылку.
Is_sym = FileTest::symlink?("myfile") # true.
Hard_count = File.new("myfile").stat.nlink # 0.
Отметим попутно, что в предыдущем примере мы воспользовались методом класса symlink из класса File для создания символической ссылки.
В редких случаях может понадобиться информация о файле еще более низкого уровня. В классе File::Stat есть еще три метода экземпляра, предоставляющих такую информацию. Метод dev возвращает целое число, идентифицирующее устройство, на котором расположен файл. Метод rdev возвращает целое число, описывающее тип устройства, а для дисковых файлов метод ino возвращает номер первого индексного узла, занятого файлом.
File = File.new("diskfile").
Info = file.stat.
Device = info.dev.
Devtype = info.rdev.
Inode = info.ino.
10.1.13. Каналы.
Ruby поддерживает разные способы читать из канала и писать в него. Метод класса IO.popen открывает канал и связывает с возвращенным объектом стандартные ввод и вывод процесса. Часто с разными концами канала работают разные потоки, но в примере ниже запись и чтение осуществляет один и тот же поток:
Check = IO.popen("spell","r+").
Check.puts("'T was brillig, and the slithy toves").
Check.puts("Did gyre and gimble in the wabe.").
Check.close_write.
List = check.readlines.
List.collect! { |x| x.chomp }.
# list равно %w[brillig gimble gyre slithy toves wabe].
Отметим, что вызов close_write обязателен, иначе мы никогда не достигнем конца файла при чтении из канала. Существует также блочная форма:
File.popen("/usr/games/fortune") do |pipe|
Quote = pipe.gets.
Puts quote.
# На чистом диске можно искать бесконечно. - Том Стил.
End.
Если задана строка "-", то запускается новый экземпляр Ruby. Если при этом задан еще и блок, то он работает в двух разных процессах, как в результате разветвления (fork); блоку в процессе-потомке передается nil, а в процессе-родителе — объект IO, с которым связан стандартный ввод или стандартный вывод.
IO.popen("-").
Do |mypipe|
if mypipe.
puts "Я родитель: pid = #{Process.pid}".
listen = mypipe.gets.
puts listen.
else.
puts "Я потомок: pid = #{Process.pid}".
end.
End.
# Печатается:
# Я родитель: pid = 10580.
# Я потомок: pid = 10582.
Метод pipe возвращает также два конца канала, связанных между собой. В следующем примере мы создаем два потока, один из которых передает сообщение другому (то самое сообщение, которое Сэмюэль Морзе впервые послал по телеграфу). Если вы не знаете, что такое потоки, обратитесь к главе 3.
Pipe = IO.pipe.
Reader = pipe[0].
Writer = pipe[1].
Str = nil.
Thread1 = Thread.new(reader,writer) do |reader,writer|
# writer.close_write.
str = reader.gets.
reader.close.
End.
Thread2 = Thread.new(reader,writer) do |reader,writer|
# reader.close_read.
writer.puts("What hath God wrought?").
writer.close.
End.
Thread1.join.
Thread2.join.
Puts str # What hath God wrought?
10.1.14. Специальные операции ввода/вывода.
В Ruby можно выполнять низкоуровневые операции ввода/вывода. Мы только упомянем о существовании таких методов; если вы собираетесь ими пользоваться, имейте в виду, что некоторые машиннозависимы (различаются даже в разных версиях UNIX).
Метод ioctl принимает два аргумента: целое число, определяющее операцию, и целое число либо строку, представляющую параметр этой операции.
Метод fcntl также предназначен для низкоуровневого управления файловыми потоками системно зависимым образом. Он принимает такие же параметры, как ioctl.
Метод select (в модуле Kernel) принимает до четырех параметров. Первый из них — массив дескрипторов для чтения, а остальные три необязательны (массив дескрипторов для записи, дескрипторов для ошибок и величина тайм-аута). Если на каком-то из устройств, дескрипторы которых заданы в первом массиве, оказываются новые данные для чтения или какое-то из устройств, дескрипторы которых перечислены во втором массиве, готово к выполнению записи, метод возвращает массив из трех элементов, каждый из которых в свою очередь является массивом, где указаны дескрипторы устройств, готовых к выполнению ввода/вывода.
Метод syscall из модуля Kernel принимает по меньшей мере один целочисленный параметр (а всего до девяти целочисленных или строковых параметров). Первый параметр определяет выполняемую операцию ввода/вывода.
Метод fileno возвращает обычный файловый дескриптор, ассоциированный с потоком ввода/вывода. Это наименее системно зависимый из всех перечислениях выше методов.
Desc = $stderr.fileno # 2.
10.1.15. Неблокирующий ввод/вывод.
«За кулисами» Ruby предпринимает согласованные меры, чтобы операции ввода/вывода не блокировали выполнение программы. В большинстве случаев для управления вводом/выводом можно пользоваться потоками — один поток может выполнить блокирующую операцию, а второй будет продолжать работу.
Это немного противоречит интуиции. Потоки Ruby работают в том же процессе, они не являются платформенными потоками. Быть может, вам кажется, что блокирующая операция ввода/вывода должна приостанавливать весь процесс, а значит, и все его потоки. Это не так — Ruby аккуратно управляет вводом/выводом прозрачно для программиста.
Но если вы все же хотите включить неблокирующий режим ввода/вывода, такая возможность есть. Небольшая библиотека io/nonblock предоставляет методы чтения и установки для объекта IO, представляющего блочное устройство:
Require 'io/nonblock'.
# ...
Test = mysock.nonblock? # false.
Mysock.nonblock = true # Отключить блокирующий режим.
# ...
Mysock.nonblock = false # Снова включить его.
Mysock.nonblock { some_operation(mysock) }.
# Выполнить some_operation в неблокирующем режиме.
Mysock.nonblock(false) { other_operation(mysock) }.
# Выполнить other_operation в блокирующем режиме.
10.1.16. Применение метода readpartial.
Метод readpartial появился сравнительно недавно с целью упростить ввод/вывод при определенных условиях. Он может использоваться с любыми потоками, например с сокетами.
Параметр «максимальная длина» (max length) обязателен. Если задан параметр buffer, то он должен ссылаться на строку, в которой будут храниться данные.
Data = sock.readpartial(128) # Читать не более 128 байтов.
Метод readpartial игнорирует установленный режим блокировки ввода/вывода. Он может блокировать программу, но лишь при выполнении следующих условий: буфер объекта IO пуст, в потоке ничего нет и поток еще не достиг конца файла.
Таким образом, если в потоке есть данные, то readpartial не будет блокировать программу. Он читает не более указанного числа байтов, а если байтов оказалось меньше, то прочитает их и продолжит выполнение.
Если в потоке нет данных, но при этом достигнут конец файла, то readpartial немедленно возбуждает исключение EOFError.
Если вызов блокирующий, то он ожидает, пока не произойдет одно из двух событий: придут новые данные или обнаружится конец файла. Если поступают данные, метод возвращает их вызывающей программе, а в случае обнаружения конца файла возбуждает исключение EOFError.
При вызове метода sysread в блокирующем режиме он ведет себя похоже на readpartial. Если буфер пуст, их поведение вообще идентично.
10.1.17. Манипулирование путевыми именами.
Основными методами для работы с путевыми именами являются методы класса File.dirname и File.basename; они работают, как одноименные команды UNIX, то есть возвращают имя каталога и имя файла соответственно. Если вторым параметром методу basename передана строка с расширением имени файла, то это расширение исключается.
Str = "/home/dave/podbay.rb".
Dir = File.dirname(str) # "/home/dave".
File1 = File.basename(str) # "podbay.rb".
File2 = File.basename(str,".rb") # "podbay".
Хотя это методы класса File, на самом деле они просто манипулируют строками.
Упомянем также метод File.split, который возвращает обе компоненты (имя каталога и имя файла) в массиве из двух элементов:
Info = File.split(str) # ["/home/dave","podbay.rb"].
Метод класса expand_path преобразует путевое имя в абсолютный путь. Если операционная система понимает сокращения ~ и ~user, то они тоже учитываются.
Dir.chdir("/home/poole/personal/docs").
Abs = File.expand_path("../../misc") # "/home/poole/misc".
Если передать методу path открытый файл, то он вернет путевое имя, по которому файл был открыт.
File = File.new("../../foobar").
Name = file.path # "../../foobar".
Константа File::Separator равна символу, применяемому для разделения компонентов путевого имени (в Windows это обратная косая черта, а в UNIX — прямая косая черта). Имеется также синоним File::SEPARATOR.
Метод класса join использует этот разделитель для составления полного путевого имени из переданного списка компонентов:
Path = File.join("usr","local","bin","someprog").
# path равно "usr/local/bin/someprog".
# Обратите внимание, что в начало имени разделитель не добавляется!
Не думайте, что методы File.join и File.split взаимно обратны, — это не так.
10.1.18. Класс Pathname.
Следует знать о существовании стандартной библиотеки pathname, которая предоставляет класс Pathname. В сущности, это обертка вокруг классов Dir, File, FileTest и FileUtils, поэтому он комбинирует многие их функции логичным и интуитивно понятным способом.
Path = Pathname.new("/home/hal").
File = Pathname.new("file.txt").
P2 = path + file.
Path.directory? # true.
Path.file? # false.
P2.directory? # false.
P2.file? # true.
Parts = path2.split # [Путевое имя:/home/hal, Путевое имя:file.txt].
Ext = path2.extname # .txt.
Как и следовало ожидать, имеется ряд вспомогательных методов. Метод root? пытается выяснить, относится ли данный путь к корневому каталогу, но его можно «обмануть», так как он просто анализирует строку, не обращаясь к файловой системе. Метод parent? возвращает путевое имя родительского каталога данного пути. Метод children возвращает непосредственных потомков каталога, заданного своим путевым именем; в их число включаются как файлы, так и каталоги, но рекурсивного спуска не производится.
P1 = Pathname.new("//") # Странно, но допустимо.
P1.root? # true.
Р2 = Pathname.new("/home/poole").
P3 = p2.parent # Путевое имя:/home.
Items = p2.children # Массив объектов Pathname.
# (все файлы и каталоги, являющиеся.
# непосредственными потомками р2).
Как и следовало ожидать, методы relative и absolute пытаются определить, является ли путь относительным или абсолютным (проверяя, есть ли в начале имени косая черта):
P1 = Pathname.new("/home/dave").
P1.absolute? # true.
P1.relative? # false.
Многие методы, например size, unlink и пр., просто делегируют работу классам File, FileTest и FileUtils; повторно функциональность не реализуется.
Дополнительную информацию о классе Pathname вы найдете на сайте ruby-doc.org или в любом другом справочном руководстве.
10.1.19. Манипулирование файлами на уровне команд.
Часто приходится манипулировать файлами так, как это делается с помощью командной строки: копировать, удалять, переименовывать и т.д.
Многие из этих операций реализованы встроенными методами, некоторые находятся в модуле FileUtils из библиотеки fileutils. Имейте в виду, что раньше функциональность модуля FileUtils подмешивалась прямо в класс File; теперь эти методы помещены в отдельный модуль.
Для удаления файла служит метод File.delete или его синоним File.unlink:
File.delete("history").
File.unlink("toast").
Переименовать файл позволяет метод File.rename:
File.rename("Ceylon","SriLanka").
Создать ссылку на файл (физическую или символическую) позволяют методы File.link и File.symlink соответственно:
File.link("/etc/hosts","/etc/hostfile") # Физическая ссылка.
File.symlink("/etc/hosts","/tmp/hosts") # Символическая ссылка.
Файл можно усечь до нулевой длины (или до любой другой), воспользовавшись методом экземпляра truncate:
File.truncate("myfile",1000) # Теперь не более 1000 байтов.
Два файла можно сравнить с помощью метода compare_file. У него есть синонимы cmp и compare_stream:
Require "fileutils".
Same = FileUtils.compare_file("alpha","beta") # true.
Метод copy копирует файл в другое место, возможно, с переименованием. У него есть необязательный флаг, говорящий, что сообщения об ошибках нужно направлять на стандартный вывод для ошибок. Синоним — привычное для программистов UNIX имя cp.
Require "fileutils".
# Скопировать файл epsilon в theta с протоколированием ошибок.
FileUtils.сору("epsilon","theta", true).
Файл можно перемещать методом move (синоним mv). Как и сору, этот метод имеет необязательный параметр, включающий вывод сообщений об ошибках.
Require "fileutils".
FileUtils.move( "/trap/names", "/etc") # Переместить в другой каталог.
FileUtils.move("colours","colors") # Просто переименовать.
Метод safe_unlink удаляет один или несколько файлов, предварительно пытаясь сделать их доступными для записи, чтобы избежать ошибок. Если последний параметр равен true или false, он интерпретируется как флаг, задающий режим вывода сообщений об ошибках.
Require "fileutils".
FileUtils.safe_unlink("alpha","beta","gamma").
# Протоколировать ошибки при удалении следующих двух файлов:
FileUtils.safe_unlink("delta","epsilon",true).
Наконец, метод install делает практически то же, что и syscopy, но сначала проверяет, что целевой файл либо не существует, либо содержит такие же данные.
Require "fileutils".
FileUtils.install("foo.so","/usr/lib").
# Существующий файл foo.so не будет переписан,
# если он не отличается от нового.
Дополнительную информацию о модуле FileUtils см. на сайте ruby-doc.org или в любом другом справочном руководстве.
10.1.20. Ввод символов с клавиатуры.
В данном случае мы имеем в виду небуферизованный ввод, когда символ обрабатывается сразу после нажатия клавиши, не дожидаясь, пока будет введена вся строка.
Это можно сделать и в UNIX, и в Windows, но, к сожалению, совершенно по-разному.
Версия для UNIX прямолинейна. Мы переводим терминал в режим прямого ввода (raw mode) и обычно одновременно отключаем эхо-контроль.
Def getchar.
system("stty raw -echo") # Прямой ввод без эхо-контроля.
char = STDIN.getc.
system("stty -raw echo") # Восстановить режим терминала.
char.
End.
На платформе Windows придется написать расширение на С. Пока что альтернативой является использование одной из функций в библиотеке Win32API.
Require 'Win32API'.
Def getchar.
char = Win32API.new("crtdll", "_getch", [], 'L').Call.
End.
Поведение в обоих случаях идентично.
10.1.21. Чтение всего файла в память.
Чтобы прочитать весь файл в массив, не нужно даже его предварительно открывать. Все сделает метод IO.readlines: откроет файл, прочитает и закроет.
Arr = IO.readlines("myfile").
Lines = arr.size.
Puts "myfile содержит #{lines} строк.".
Longest = arr.collect {|x| x.length}.max.
Puts "Самая длинная строка содержит #{longest} символов.".
Можно также воспользоваться методом IO.read (который возвращает одну большую строку, а не массив строк).
Str = IO.read("myfile").
Bytes = arr.size.
Puts "myfile содержит #{bytes} байтов.".
Longest=str.collect {|x| x.length}.max # строки - перечисляемые объекты!
Puts "Самая длинная строка содержит #{longest} символов.".
Поскольку класс IO является предком File, то можно вместо этого писать File.deadlines и File.read.
10.1.22. Построчное чтение из файла.
Чтобы читать по одной строке из файла, можно обратиться к методу класса IO.foreach или к методу экземпляра each. В первом случае файл не нужно явно открывать.
# Напечатать все строки, содержащие слово "target".
IO.foreach("somefile") do |line|
puts line if line =~ /target/
End.
# Другой способ...
File = File.new("somefile").
File.each do |line|
puts line if line =~ /target/
End.
Отметим, что each_line — синоним each.
10.1.23. Побайтное чтение из файла.
Для чтения из файла по одному байту служит метод экземпляра each_byte. Напомним, что он передает в блок символ (то есть целое число); воспользуйтесь методом chr, если хотите преобразовать его в «настоящий» символ.
File = File.new("myfile").
E_count = 0.
File.each_byte do |byte|
e_count += 1 if byte == ?e.
End.
10.1.24. Работа со строкой как с файлом.
Иногда возникает необходимость рассматривать строку как файл. Что под этим понимается, зависит от конкретной задачи.
Объект определяется прежде всего своими методами. В следующем фрагменте показано, как к объекту source применяется итератор; на каждой итерации выводится одна строка. Можете ли вы что-нибудь сказать о типе объекта source, глядя на этот код?
Source.each do |line|
puts line.
End.
Это могли бы быть как файл, так и строка, содержащая внутри символы новой строки. В таких случаях строку можно трактовать как файл без всякого труда.
В последних версиях Ruby имеется также библиотека stringio.
Интерфейс класса StringIO практически такой же, как в первом издании этой книги. В нем есть метод доступа string, ссылающийся на содержимое самой строки.
Require 'stringio'.
Ios = StringIO.new("abcdefghijkl\nABC\n123").
Ios.seek(5).
Ios.puts("xyz").
Puts ios.tell # 8.
Puts ios.string.dump # "abcdexyzijkl\nABC\n123".
С = ios.getc.
Puts "с = #{c}" # с = 105.
Ios.ungetc(?w).
Puts ios.string.dump # "abcdexyzwjkl\nABC\n123".
Puts "Ptr = #{ios.tell}".
S1 = ios.gets # "wjkl".
S2 = ios.gets # "ABC".
10.1.25. Чтение данных, встроенных в текст программы.
Когда подростком вы учили язык BASIC, копируя программы из журналов, то, наверное, для удобства часто пользовались предложением DATA. Оно позволяло включать информацию прямо в текст программы, но читать ее так, будто она поступает из внешнего источника.
При желании то же самое можно сделать и в Ruby. Директива __END__ в конце программы говорит, что дальше идут встроенные данные. Их можно читать из глобальной константы DATA, которая представляет собой обычный объект IO. (Отметим, что маркер __END__ должен располагаться с начала строки.).
# Распечатать все строки "задом наперед"...
DATA.each_line do |line|
puts line.reverse.
End.
__END__
A man, a plan, a canal... Panama!
Madam, I'm Adam.
,siht daer nac uoy fI.
.drah oot gnikrow neeb ev'uoy.
10.1.26. Чтение исходного текста программы.
Если вы хотите получить доступ к исходному тексту собственной программы, то можете воспользоваться уже описанным выше трюком (см. раздел 10.1.25).
Глобальная константа DATA — это объект класса IO, ссылающийся на данные, которые расположены после директивы __END__. Но если выполнить метод rewind, то указатель файла будет переустановлен на начало текста программы.
Следующая программа выводит собственный текст, снабжая его номерами строк. Это не очень полезно, но, быть может, вы найдете и другие применения такой техники.
DATA.rewind.
Num = 1.
DATA.each_line do |line|
puts "#{'%03d' % num} #{line}".
num += 1.
End.
__END__
Отметим, что наличие директивы __END__ обязательно — без нее к константе DATA вообще нельзя обратиться.
10.1.27. Работа с временными файлами.
Во многих случаях необходимо работать с файлами, которые по сути своей анонимны. Мы не хотим возиться с присваиванием им имен и проверять, что при этом не возникает конфликтов с существующими файлами. И помнить о том, что такие файлы нужно удалять, тоже не хочется.
Все эти проблемы решает библиотека Tempfile. Метод new (синоним open) принимает базовое имя в качестве строки-затравки и конкатенирует его с идентификатором процесса и уникальным порядковым номером. Необязательный второй параметр — имя каталога, в котором создается временный файл; по умолчанию оно равно значению первой из существующих переменных окружения tmpdir, tmp или temp, а если ни одна из них не задана, то "/tmp".
Возвращаемый объект IO можно многократно открывать и закрывать на протяжении всей работы программы, а по ее завершении временный файл будет автоматически удален.
У метода close есть необязательный флаг; если он равен true, то файл удаляется сразу после закрытия (не дожидаясь завершения программы). Метод path возвращает полное имя файла, если оно вам по какой-то причине понадобится.
Require "tempfile".
Temp = Tempfile.new("stuff").
Name = temp.path # "/tmp/stuff17060.0".
Temp.puts "Здесь был Вася".
Temp.close.
# Позже...
Temp.open.
Str = temp.gets # "Здесь был Вася".
Temp.close(true) # Удалить СЕЙЧАС.
10.1.28. Получение и изменение текущего каталога.
Получить имя текущего каталога можно с помощью метода Dir.pwd (синоним Dir.getwd). Эти имена уже давно употребляются как сокращения от «print working directory» (печатать рабочий каталог) и «get working directory» (получить рабочий каталог). На платформе Windows символы обратной косой черты преобразуются в символы прямой косой черты.
Для изменения текущего каталога служит метод Dir.chdir. В Windows в начале строки можно указывать букву диска.
Dir.chdir("/var/tmp").
Puts Dir.pwd # "/var/tmp".
Puts Dir.getwd # "/var/tmp".
Этот метод также принимает блок в качестве параметра. Если блок задан, то текущий каталог изменяется только на время выполнения блока, а потом восстанавливается первоначальное значение:
Dir.chdir("/home").
Dir.chdir("/tmp") do.
puts Dir.pwd # /tmp.
# Какой-то код...
End.
Puts Dir.pwd # /home.
10.1.29. Изменение текущего корня.
В большинстве систем UNIX можно изменить «представление» процесса о том, что такое корневой каталог /. Обычно это делается из соображений безопасности перед запуском небезопасной или непротестированной программы. Метод chroot делает указанный каталог новым корнем:
Dir.chdir("/home/guy/sandbox/tmp").
Dir.chroot("/home/guy/sandbox").
Puts Dir.pwd # "/tmp".
10.1.30. Обход каталога.
Метод класса foreach — это итератор, который последовательно передает в блок каждый элемент каталога. Точно так же ведет себя метод экземпляра each.
Dir.foreach("/tmp") { |entry| puts entry }.
Dir = Dir.new("/tmp").
Dir.each { |entry| puts entry }.
Оба фрагмента печатают одно и то же (имена всех файлов и подкаталогов в каталоге /tmp).
10.1.31. Получение содержимого каталога.
Метод класса Dir.entries возвращает массив, содержащий все элементы указанного каталога:
List = Dir.entries("/tmp") # %w[. .. alpha.txt beta.doc].
Как видите, включаются и элементы, соответствующие текущему и родительскому каталогу. Если они вам не нужны, придется отфильтровать их вручную.
10.1.32. Создание цепочки каталогов.
Иногда необходимо создать глубоко вложенный каталог, причем промежуточные каталоги могут и не существовать. В UNIX мы воспользовались бы для этого командой mkdir -p.
В программе на Ruby такую операцию выполняет метод FileUtils.makedirs (из библиотеки fileutils):
Require "fileutils".
FileUtils.makedirs("/tmp/these/dirs/need/not/exist").
10.1.33. Рекурсивное удаление каталога.
В UNIX команда rm -rf dir удаляет все поддерево начиная с каталога dir. Понятно, что применять ее надо с осторожностью.
В последних версиях Ruby в класс Pathname добавлен метод rmtree, решающий ту же задачу. В модуле FileUtils есть аналогичный метода rm_r.
Require 'pathname'.
Dir = Pathname.new("/home/poole/").
Dir.rmtree.
# или:
Require 'fileutils'.
FileUtils.rm_r("/home/poole").
10.1.34. Поиск файлов и каталогов.
Ниже мы воспользовались стандартной библиотекой find.rb для написания метода, который находит один или более файлов и возвращает их список в виде массива. Первый параметр — это начальный каталог, второй — либо имя файла (строка), либо регулярное выражение.
Require "find".
Def findfiles(dir, name).
list = [].
Find.find(dir) do |path|
Find.prune if [".",".."].include? Path.
case name.
when String.
list << path if File.basename(path) == name.
when Regexp.
list << path if File.basename(path) =~ name.
else.
raise ArgumentError.
end.
end.
list.
End.
Findfiles "/home/hal", "toc.txt".
# ["/home/hal/docs/toc.txt", "/home/hal/misc/toc.txt"].
Findfiles "/home", /^[a-z]+.doc/
# ["/home/hal/docs/alpha.doc", "/home/guy/guide.doc",
# "/home/bill/help/readme.doc"].
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.
- 188.
- 189.
- 190.
- 191.
- 192.
- 193.
- 194.
- 195.
- 196.
- 197.
- 198.
- 199.
- 200.
- 201.
- 202.
- 203.
- 204.
- 205.
- 206.
- 207.
- 208.
- 209.
- 210.
- 211.
- 212.
- 213.
- 214.
- 215.
- 216.
- 217.
- 218.
- 219.
- 220.
- 221.
- 222.
- 223.
- 224.
- 225.
- 226.
- 227.
- 228.
- 229.
- 230.
- 231.
- 232.
- 233.
- 234.
- 235.
- 236.
- 237.
- 238.
- 239.
- 240.
- 241.
- 242.
- 243.
- 244.
- 245.
- 246.
- 247.
- 248.
- 249.
- 250.
- 251.
- 252.
- 253.
- 254.
- 255.
- 256.
- 257.
- 258.
- 259.
- 260.
- 261.
- 262.
- 263.
- 264.
- 265.
- 266.
- 267.
- 268.
- 269.
- 270.
- 271.
- 272.
- 273.
- 274.
- 275.
- 276.
- 277.
- 278.
- 279.
- 280.
- 281.
- 282.
- 283.
- 284.
- 285.
- 286.
- 287.
- 288.
- 289.
- 290.
- 291.
- 292.
- 293.
- 294.
- 295.
- 296.
- 297.
- 298.
- 299.
- 300.
- 301.
- 302.
- 303.
- 304.
- 305.
- 306.
- 307.
- 308.
- 309.
- 310.
- 311.
- 312.
- 313.
- 314.
- 315.
- 316.
- 317.
- 318.
- 319.
- 320.
- 321.
- 322.
- 323.
- 324.
- 325.
- 326.
- 327.
- 328.
- 329.
- 330.
- 331.
- 332.
- 333.
- 334.
- 335.
- 336.
- 337.
- 338.
- 339.
- 340.
- 341.
- 342.
- 343.
- 344.
- 345.
- 346.
- 347.
- 348.
- 349.
- 350.
- 351.
- 352.
- 353.
- 354.
- 355.
- 356.
- 357.
- 358.
- 359.
- 360.
- 361.
- 362.
- 363.
- 364.
- 365.
- 366.
- 367.
- 368.
- 369.
- 370.
- 371.
- 372.
- 373.
- 374.
- 375.
- 376.
- 377.
- 378.
- 379.
- 380.
- 381.
- 382.
- 383.
- 384.
- 385.
- 386.
- 387.
- 388.
- 389.
- 390.
- 391.
- 392.
- 393.
- 394.
- 395.
- 396.
- 397.
- 398.
- 399.
- 400.
- 401.
- 402.
- 403.
- 404.
- 405.
- 406.
- 407.
- 408.
- 409.
- 410.
- 411.
- 412.
- 413.
- 414.
- 415.
- 416.
- 417.
- 418.
- 419.
- 420.
- 421.
- 422.
- 423.
- 424.
- 425.
- 426.
- 427.
- 428.
- 429.
- 430.
- 431.
- 432.
- 433.
- 434.
- 435.
- 436.
- 437.
- 438.
- 439.
- 440.
- 441.
- 442.
- 443.
- 444.
- 445.
- 446.
- 447.
- 448.
- 449.
- 450.
- 451.
- 452.
- 453.
- 454.
- 455.
- 456.
- 457.
- 458.
- 459.
- 460.
- 461.
- 462.
- 463.
- 464.
- 465.
- 466.
- 467.
- 468.
- 469.
- 470.
- 471.
- 472.
- 473.
- 474.
- 475.
- 476.
- 477.
- 478.
- 479.
- 480.
- 481.
- 482.
- 483.
- 484.
- 485.
- 486.
- 487.
- 488.
- 489.
- 490.
- 491.
- 492.
- 493.
- 494.
- 495.
- 496.
- 497.
- 498.
- 499.
- 500.
- 501.
- 502.
- 503.
- 504.
- 505.
- 506.
- 507.
- 508.
- 509.
- 510.
- 511.
- 512.
- 513.
- 514.
- 515.
- 516.
- 517.
- 518.
- 519.
- 520.
- 521.
- 522.
- 523.
- 524.
- 525.
- 526.
- 527.
- 528.
- 529.
- 530.
- 531.
- 532.
- 533.
- 534.
- 535.
- 536.
- 537.
- 538.
- 539.
- 540.
- 541.
- 542.
- 543.
- 544.
- 545.
- 546.
- 547.
- 548.
- 549.
- 550.
- 551.
- 552.
- 553.
- 554.
- 555.
- 556.
- 557.
- 558.
- 559.
- 560.
- 561.
- 562.
- 563.
- 564.
- 565.
- 566.
- 567.
- 568.
- 569.
- 570.
- 571.
- 572.
- 573.