Программирование на языке Ruby - Хэл Фултон
Шрифт:
Интервал:
Закладка:
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
Это могли бы быть как файл, так и строка, содержащая внутри символы новой строки. В таких случаях строку можно трактовать как файл без всякого труда.