Программирование на языке Ruby - Хэл Фултон
Шрифт:
Интервал:
Закладка:
puts line
end
Это могли бы быть как файл, так и строка, содержащая внутри символы новой строки. В таких случаях строку можно трактовать как файл без всякого труда.
В последних версиях Ruby имеется также библиотека stringio.
Интерфейс класса StringIO практически такой же, как в первом издании этой книги. В нем есть метод доступа string, ссылающийся на содержимое самой строки.
require 'stringio'
ios = StringIO.new("abcdefghijklnABCn123")
ios.seek(5)
ios.puts("xyz")
puts ios.tell # 8
puts ios.string.dump # "abcdexyzijklnABCn123"
с = ios.getc
puts "с = #{c}" # с = 105
ios.ungetc(?w)
puts ios.string.dump # "abcdexyzwjklnABCn123"
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"]
10.2. Доступ к данным более высокого уровня
Часто возникает необходимость хранить и извлекать данные более прозрачным способом. Модуль Marshal предоставляет простые средства сохранения объектов а на его основе построена библиотека PStore. Наконец, библиотека dbm позволяет организовать нечто вроде хэша на диске. Строго говоря, она не относится к теме данного раздела, но уж слишком проста, чтобы рассказывать о ней в разделе, посвященном базам данных.