File.open('myfile','wb') {|f| f.syswrite('12345 326789
') }
#Обратите внимание на восьмеричное 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.
'
puts '#{line.size} символов.' # 10 символов,
file.close
file = File.open('data','rb')
line = file.readline # 'Строка 1.
'
puts '#{line.size} символов.' # 11 символов.
file.close
Отметим, что упомянутый в коде метод binmode
переключает поток в двоичный режим. После переключения вернуться в текстовый режим невозможно.
file = File.open('data')
file.binmode
line = file.readline # 'Строка 1.
'
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(?
) # Вывести символ новой строки.
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