$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 'Пока!
'
Если запустить эту программу, то вы увидите, что сообщения «Привет» и «Пока» появляются одновременно, после завершения sleep
. При этом первое сообщение не завершается символом новой строки.
Это можно исправить, вызвав метод flush
для опустошения буфера вывода. В данном случае вывод идет в поток $defout
(подразумеваемый по умолчанию для всех методов Kernel
, которые занимаются выводом). И поведение оказывается ожидаемым, то есть первое сообщение появляется раньше второго.
print 'Привет... '
STDOUT.flush
sleep 10
print 'Пока!
'
Буферизацию можно отключить (или включить) методом 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)