В следующем фрагменте показано, что на платформе 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) # Разделяемая блокировка (другие
#
Пытаемся заблокировать файл, но не приостанавливаем программу, если
# не получилось; в таком случае переменная 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
, хотя и напоминает таковой. По умолчанию ввод связан именно с этим объектом, если в командной строке задан хотя бы один файл.