класс IO
Экземпляр класса IO (обычно называемый потоком) представляет собой поток ввода-вывода в базовой операционной системе. Класс IO является основой ввода и вывода в Ruby.
Класс File — единственный класс в ядре Ruby, являющийся подклассом IO. Некоторые классы стандартной библиотеки Ruby также являются подклассами IO; к ним относятся TCPSocket и UDPSocket.
Глобальная константа ARGF (также доступная как $<) предоставляет поток, подобный IO, который позволяет обращаться ко всем путям к файлам, указанным в ARGV (или найденным в STDIN, если ARGV пуст). ARGF сама по себе не является подклассом IO.
Класс StringIO предоставляет поток, подобный IO, который работает со значением String. StringIO сам по себе не является подклассом IO.
К важным объектам, основанным на IO, относятся:
-
$stdin.
-
$stdout.
-
$stderr.
-
Экземпляры класса
File.
Экземпляр IO можно создать с помощью:
-
IO.new: возвращает новый объект IO для заданного целочисленного дескриптора файла. -
IO.open: передает новый объект IO заданному блоку. -
IO.popen: возвращает новый объект IO, подключенный к $stdin и $stdout вновь запущенного подпроцесса. -
Kernel#open: Возвращает новый объект IO, подключенный к заданному источнику: потоку, файлу или подпроцессу.
Как и поток File, поток IO имеет:
-
Режим чтения/записи, который может быть только для чтения, только для записи или для чтения и записи; см. Режим чтения/записи.
-
Режим данных, который может быть текстовым или двоичным; см. Режим данных.
-
Внутреннюю и внешнюю кодировки; см. Кодировки.
И, как у других потоков IO, у него есть:
-
Позиция, определяющая, где в потоке будет выполняться следующее чтение или запись; см. Позиция.
-
Номер строки — особая «позиция», ориентированная на строки (отличная от упомянутой выше позиции); см. Номер строки.
Расширение io/console
Расширение io/console предоставляет множество методов для взаимодействия с консолью; его подключение добавляет множество методов в класс IO.
Примеры файлов
Во многих примерах здесь используются следующие переменные:
# English text with newlines.
text = <<~EOT
First line
Second line
Fourth line
Fifth line
EOT
# Russian text.
russian = "\u{442 435 441 442}" # => "тест"
# Binary data.
data = "\u9990\u9991\u9992\u9993\u9994"
# Text file.
File.write('t.txt', text)
# File with Russian text.
File.write('t.rus', russian)
# File with binary data.
f = File.new('t.dat', 'wb:UTF-16')
f.write(data)
f.close
Параметры открытия
Некоторые методы IO принимают необязательные именованные аргументы, определяющие, как следует открывать новый поток:
-
:mode: режим потока. -
:flags: флаги открытия файлаInteger; если также заданmode, значения объединяются побитовой операцией ИЛИ. -
:external_encoding: внешняя кодировка потока. -
:internal_encoding: внутренняя кодировка потока.'-'— синоним кодировки по умолчанию. Если значение равноnil, преобразование не выполняется. -
:encoding: задает внешнюю и внутреннюю кодировки в виде'extern:intern'. -
:textmode: если значение истинно, задает текстовый режим, в противном случае — двоичный. -
:binmode: если значение истинно, задает двоичный режим, в противном случае — текстовый. -
:autoclose: если значение истинно, указывает, чтоfdбудет закрыт при закрытии потока; в противном случае он остается открытым. -
:path:Если задано строковое значение, оно используется вinspectи доступно через методpath.
Также доступны параметры, предлагаемые методом String#encode, которые могут управлять преобразованием между внешней и внутренней кодировками.
Основные операции IO
Для выполнения основных операций ввода-вывода с потоком используйте следующие методы, которые обычно работают со строками, состоящими из нескольких байтов:
-
IO#read: считывает и возвращает часть оставшихся байтов потока или все их. -
IO#write: записывает в поток ноль или более строк; каждый переданный объект, который еще не является строкой, преобразуется с помощьюto_s.
Позиция
Поток IO имеет неотрицательную целочисленную позицию — смещение в байтах, по которому будет выполняться следующее чтение или запись. У нового потока позиция равна нулю (как и номер строки); метод rewind сбрасывает позицию (и номер строки) в ноль.
Эти методы отбрасывают буферы и экземпляры Encoding::Converter, используемые этим объектом IO.
Соответствующие методы:
-
IO#tell(псевдонимpos): возвращает текущую позицию потока (в байтах). -
IO#pos=: задает позицию потока, равную заданному целому числуnew_position(в байтах). -
IO#seek: задает позицию потока, равную заданному целому числуoffset(в байтах) относительно заданной позицииwhence(начала, конца или текущей позиции). -
IO#rewind: перемещает поток в начало (также сбрасывая номер строки).
Открытые и закрытые потоки
Новый поток IO может быть открыт для чтения, для записи или для обеих операций.
Поток автоматически закрывается, когда сборщик мусора получает его в свое распоряжение.
Попытка чтения из закрытого потока или записи в него вызывает исключение.
Соответствующие методы:
-
IO#close: закрывает поток для чтения и записи. -
IO#close_read: закрывает поток для чтения. -
IO#close_write: закрывает поток для записи. -
IO#closed?: возвращает, закрыт ли поток.
Конец потока
Можно проверить, находится ли поток в конце:
Переместиться в конец потока можно с помощью метода IO#seek:
f = File.new('t.txt')
f.eof? # => false
f.seek(0, :END)
f.eof? # => true
f.close
Или прочитав все содержимое потока (это медленнее, чем использовать IO#seek):
f.rewind f.eof? # => false f.read # => "First line\nSecond line\n\nFourth line\nFifth line\n" f.eof? # => true
Построчные операции IO
Класс IO поддерживает построчный ввод и вывод.
Построчный ввод
Класс IO поддерживает построчный ввод из файлов и потоков IO.
Построчный ввод из файла
Читать строки из файла можно с помощью следующих методов:
-
IO.foreach: считывает каждую строку и передает ее заданному блоку. -
IO.readlines: считывает и возвращает все строки в массиве.
Для каждого из этих методов:
-
Можно задать параметры открытия.
-
Разбор строк зависит от действующего разделителя строк; см. Разделитель строк.
-
Длина каждой возвращаемой строки зависит от действующего ограничения длины строки; см. Ограничение длины строки.
Построчный ввод из потока
Читать строки из потока IO можно с помощью следующих методов:
-
IO#each_line: считывает каждую оставшуюся строку и передает ее заданному блоку. -
IO#gets: возвращает следующую строку. -
IO#readline: аналогиченgets, но вызывает исключение в конце потока. -
IO#readlines: возвращает все оставшиеся строки в массиве.
Для каждого из этих методов:
-
Чтение может начаться в середине строки в зависимости от позиции потока; см. Позиция.
-
Разбор строк зависит от действующего разделителя строк; см. Разделитель строк.
-
Длина каждой возвращаемой строки зависит от действующего ограничения длины строки; см. Ограничение длины строки.
Разделитель строк
Каждый из методов построчного ввода использует разделитель строк — строку, определяющую, что считается строкой; иногда его называют разделителем входных записей.
Разделитель строк по умолчанию берется из глобальной переменной $/, начальное значение которой — "\n".
Обычно следующая считываемая строка — это все данные от текущей позиции до следующего разделителя строк (см. также Особые значения разделителя строк):
f = File.new('t.txt')
# Method gets with no sep argument returns the next line, according to $/.
f.gets # => "First line\n"
f.gets # => "Second line\n"
f.gets # => "\n"
f.gets # => "Fourth line\n"
f.gets # => "Fifth line\n"
f.close
Использовать другой разделитель строк можно, передав аргумент sep:
f = File.new('t.txt')
f.gets('l') # => "First l"
f.gets('li') # => "ine\nSecond li"
f.gets('lin') # => "ne\n\nFourth lin"
f.gets # => "e\n"
f.close
Или задав глобальную переменную $/:
f = File.new('t.txt')
$/ = 'l'
f.gets # => "First l"
f.gets # => "ine\nSecond l"
f.gets # => "ine\n\nFourth l"
f.close
Особые значения разделителя строк
Каждый из методов построчного ввода принимает два особых значения параметра sep:
-
nil: весь поток считывается («целиком») в одну строку:f = File.new('t.txt') f.gets(nil) # => "First line\nSecond line\n\nFourth line\nFifth line\n" f.close -
''(пустая строка): считывается следующий «абзац» (абзацы разделяются двумя последовательными разделителями строк):f = File.new('t.txt') f.gets('') # => "First line\nSecond line\n\n" f.gets('') # => "Fourth line\nFifth line\n" f.close
Ограничение длины строки
Каждый из методов построчного ввода использует целочисленное ограничение длины строки, которое ограничивает число возвращаемых байтов. (Многобайтовый символ не разделяется, поэтому возвращаемая строка может быть немного длиннее ограничения.)
Значение ограничения по умолчанию — -1; любое отрицательное значение означает отсутствие ограничения.
Если ограничение отсутствует, строка определяется только значением sep.
# Text with 1-byte characters.
File.open('t.txt') {|f| f.gets(1) } # => "F"
File.open('t.txt') {|f| f.gets(2) } # => "Fi"
File.open('t.txt') {|f| f.gets(3) } # => "Fir"
File.open('t.txt') {|f| f.gets(4) } # => "Firs"
# No more than one line.
File.open('t.txt') {|f| f.gets(10) } # => "First line"
File.open('t.txt') {|f| f.gets(11) } # => "First line\n"
File.open('t.txt') {|f| f.gets(12) } # => "First line\n"
# Text with 2-byte characters, which will not be split.
File.open('t.rus') {|f| f.gets(1).size } # => 1
File.open('t.rus') {|f| f.gets(2).size } # => 1
File.open('t.rus') {|f| f.gets(3).size } # => 2
File.open('t.rus') {|f| f.gets(4).size } # => 2
Разделитель строк и ограничение длины
При указании аргументов sep и limit объединяются два способа поведения:
-
Возвращается следующая строка, определяемая разделителем строк
sep. -
При этом возвращается не больше байтов, чем допускает ограничение
limit.
Пример:
File.open('t.txt') {|f| f.gets('li', 20) } # => "First li"
File.open('t.txt') {|f| f.gets('li', 2) } # => "Fi"
Номер строки
Читаемый поток IO имеет неотрицательный целочисленный номер строки:
-
IO#lineno: возвращает номер строки. -
IO#lineno=: сбрасывает и возвращает номер строки.
Если он не изменен вызовом метода IO#lineno=, номер строки соответствует числу строк, прочитанных определенными построчными методами с учетом действующего разделителя строк:
-
IO.foreach: увеличивает номер строки при каждом вызове блока. -
IO#each_line: увеличивает номер строки при каждом вызове блока. -
IO#gets: увеличивает номер строки. -
IO#readline: увеличивает номер строки. -
IO#readlines: увеличивает номер строки для каждой прочитанной строки.
У нового потока номер строки изначально равен нулю (как и позиция); метод rewind сбрасывает номер строки (и позицию) в ноль:
f = File.new('t.txt')
f.lineno # => 0
f.gets # => "First line\n"
f.lineno # => 1
f.rewind
f.lineno # => 0
f.close
Чтение строк из потока обычно изменяет его номер строки:
f = File.new('t.txt', 'r')
f.lineno # => 0
f.readline # => "This is line one.\n"
f.lineno # => 1
f.readline # => "This is the second line.\n"
f.lineno # => 2
f.readline # => "Here's the third line.\n"
f.lineno # => 3
f.eof? # => true
f.close
Перебор строк потока обычно изменяет его номер строки:
File.open('t.txt') do |f|
f.each_line do |line|
p "position=#{f.pos} eof?=#{f.eof?} lineno=#{f.lineno}"
end
end
Вывод:
"position=11 eof?=false lineno=1" "position=23 eof?=false lineno=2" "position=24 eof?=false lineno=3" "position=36 eof?=false lineno=4" "position=47 eof?=true lineno=5"
В отличие от позиции потока, номер строки не влияет на место следующего чтения или записи:
f = File.new('t.txt')
f.lineno = 1000
f.lineno # => 1000
f.gets # => "First line\n"
f.lineno # => 1001
f.close
С номером строки связана глобальная переменная $.:
-
При открытии потока значение
$.не задается; в ней сохраняется значение, оставшееся от предыдущих действий в процессе:$. = 41 f = File.new('t.txt') $. = 41 # => 41 f.close -
При чтении потока значение
$.устанавливается в номер строки этого потока:f0 = File.new('t.txt') f1 = File.new('t.dat') f0.readlines # => ["First line\n", "Second line\n", "\n", "Fourth line\n", "Fifth line\n"] $. # => 5 f1.readlines # => ["\xFE\xFF\x99\x90\x99\x91\x99\x92\x99\x93\x99\x94"] $. # => 1 f0.close f1.close -
Методы
IO#rewindиIO#seekне влияют на$.:f = File.new('t.txt') f.readlines # => ["First line\n", "Second line\n", "\n", "Fourth line\n", "Fifth line\n"] $. # => 5 f.rewind f.seek(0, :SET) $. # => 5 f.close
Построчный вывод
Записывать данные в поток IO построчно можно с помощью следующего метода:
-
IO#puts: записывает объекты в поток.
Посимвольные операции IO
Обрабатывать поток IO посимвольно можно с помощью следующих методов:
-
IO#getc: считывает и возвращает следующий символ из потока. -
IO#readchar: аналогиченgetc, но вызывает исключение в конце потока. -
IO#ungetc: возвращает символ или целое число обратно в поток («сдвигает» его назад). -
IO#putc: записывает символ в поток. -
IO#each_char: считывает каждый оставшийся символ в потоке и передает его заданному блоку.
Побайтовые операции IO
Обрабатывать поток IO побайтово можно с помощью следующих методов:
-
IO#getbyte: возвращает следующий 8-битный байт в виде целого числа в диапазоне 0..255. -
IO#readbyte: аналогиченgetbyte, но вызывает исключение в конце потока. -
IO#ungetbyte: возвращает байт обратно в поток («сдвигает» его назад). -
IO#each_byte: считывает каждый оставшийся байт в потоке и передает его заданному блоку.
Операции IO с кодовыми точками
Поток IO можно обрабатывать по одной кодовой точке за раз:
-
IO#each_codepoint: считывает каждую оставшуюся кодовую точку и передает ее заданному блоку.
Содержание раздела
Сначала рассмотрим, что находится в других разделах. Класс IO:
-
Наследуется от класса Object.
-
Подключает модуль Enumerable, предоставляющий десятки дополнительных методов.
В этом разделе класс IO предоставляет методы, полезные для:
Создание
-
::new(псевдоним::for_fd): создает и возвращает новый объект IO для заданного целочисленного дескриптора файла. -
::open: создает новый объект IO. -
::pipe: создает связанную пару объектов IO для чтения и записи. -
::popen: создает объект IO для взаимодействия с подпроцессом. -
::select: выбирает переданные экземпляры IO, готовые к чтению или записи либо имеющие ожидающие обработки исключения.
Чтение
-
::binread: возвращает двоичную строку со всеми байтами заданного файла или их частью. -
::read: возвращает строку со всеми байтами заданного файла или их частью. -
::readlines: возвращает массив строк, содержащих строки заданного файла. -
getbyte: возвращает следующий 8-битный байт, прочитанный изself, в виде целого числа. -
getc: возвращает следующий символ, прочитанный изself, в виде строки. -
gets: возвращает строку, прочитанную изself. -
pread: возвращает все байты или следующие n байтов, прочитанных изself, не изменяя смещение получателя. -
read: возвращает все оставшиеся байты или следующие n байтов, прочитанных изself, если задано n. -
read_nonblock: считывает следующие n байтов изselfпри заданном n в неблокирующем режиме. -
readbyte: возвращает следующий байт, прочитанный изself; аналогиченgetbyte, но вызывает исключение в конце потока. -
readchar: возвращает следующий символ, прочитанный изself; аналогиченgetc, но вызывает исключение в конце потока. -
readline: возвращает следующую строку, прочитанную изself; аналогичен getline, но вызывает исключение в конце потока. -
readlines: возвращает массив всех строк, прочитанных изself. -
readpartial: возвращает изselfдо заданного количества байтов.
Запись
-
::binwrite: записывает заданную строку в файл по указанному пути в двоичном режиме. -
::write: записывает заданную строку вself. -
<<: добавляет заданную строку вself. -
print: выводит последнюю прочитанную строку или заданные объекты вself. -
printf: записывает данные вselfна основе заданной строки формата и объектов. -
putc: записывает символ вself. -
puts: записывает строки вself, добавляя в конец каждой строки символ новой строки. -
pwrite: записывает заданную строку по указанному смещению, не изменяя смещение получателя. -
write: записывает одну или несколько заданных строк вself. -
write_nonblock: записывает одну или несколько заданных строк вselfв неблокирующем режиме.
Позиционирование
-
lineno: возвращает текущий номер строки вself. -
lineno=: задает номер строки вself. -
pos(псевдонимtell): возвращает текущее смещение в байтах вself. -
pos=: задает смещение в байтах вself. -
reopen: связываетselfс новым или существующим потоком IO. -
rewind: перемещаетselfв начало ввода. -
seek: задает смещение дляselfотносительно указанной позиции.
Итерирование
-
::foreach: передает блоку каждую строку заданного файла. -
each(псевдонимeach_line): вызывает заданный блок для каждой следующей строки вself. -
each_byte: вызывает заданный блок для каждого следующего байта вself, передавая его как целое число. -
each_char: вызывает заданный блок для каждого следующего символа вself, передавая его как строку. -
each_codepoint: вызывает заданный блок для каждой следующей кодовой точки вself, передавая ее как целое число.
Настройки
-
autoclose=: задает, будет лиselfзакрываться автоматически. -
binmode: переводитselfв двоичный режим. -
close: закрываетself. -
close_on_exec=: задает флаг close-on-exec. -
close_read: закрываетselfдля чтения. -
close_write: закрываетselfдля записи. -
set_encoding: задает кодировку дляself. -
set_encoding_by_bom: задает кодировку дляselfна основе метки порядка байтов Unicode. -
sync=: задает режим синхронизации указанным значением.
Проверка свойств
-
autoclose?: возвращает, закрывается лиselfавтоматически. -
binmode?: возвращает, находится лиselfв двоичном режиме. -
close_on_exec?: возвращает флаг close-on-exec дляself. -
closed?: возвращает, закрыт лиself. -
eof?(псевдонимeof): возвращает, достиг лиselfконца потока. -
external_encoding: возвращает объект внешней кодировки дляself. -
fileno(псевдонимto_i): возвращает целочисленный дескриптор файла дляself. -
internal_encoding: возвращает объект внутренней кодировки дляself. -
pid: возвращает идентификатор процесса дочернего процесса, связанного сself, еслиselfбыл создан методом::popen. -
stat: возвращает объектFile::Stat, содержащий сведения о состоянииself. -
sync: возвращает, находится лиselfв режиме синхронизации. -
tty?(псевдонимisatty): возвращает, является лиselfтерминалом.
Буферизация
-
fdatasync: немедленно записывает на диск все буферизованные данные изself. -
flush: передает все буферизованные данные изselfбазовой операционной системе. -
fsync: немедленно записывает на диск все буферизованные данные и атрибуты изself. -
ungetbyte: добавляет в начало буфера дляselfзаданный байт-целое число или строку. -
ungetc: добавляет заданную строку в начало буфера дляself.
Низкоуровневый доступ
-
::sysopen: Открывает файл, указанный по пути, и возвращает целочисленный дескриптор файла. -
advise: Сообщает о намерении получить доступ к данным изselfопределённым способом. -
fcntl: Передаёт низкоуровневую команду файлу, указанному заданным дескриптором файла. -
ioctl: Передаёт низкоуровневую команду устройству, указанному заданным дескриптором файла. -
sysread: Возвращает до следующих n байтов, прочитанных из self с помощью низкоуровневого чтения. -
sysseek: Устанавливает смещение дляself. -
syswrite: Записывает заданную строку вselfс помощью низкоуровневой записи.
Прочее
-
::copy_stream: Копирует данные из источника в назначение, каждое из которых представляет собой путь к файлу или объект, подобный IO. -
::try_convert: Возвращает новый объект IO, полученный в результате преобразования заданного объекта. -
inspect: Возвращает строковое представлениеself.
Константы
- EWOULDBLOCKWaitReadable
-
то же, что и
IO::EAGAINWaitReadable - EWOULDBLOCKWaitWritable
-
то же, что и
IO::EAGAINWaitWritable - PRIORITY
-
Маска события приоритета для
IO#wait. - READABLE
-
Маска события готовности к чтению для
IO#wait. - SEEK_CUR
-
Устанавливает позицию ввода-вывода относительно текущей позиции
- SEEK_DATA
-
Устанавливает позицию ввода-вывода на следующее местоположение, содержащее данные
- SEEK_END
-
Устанавливает позицию ввода-вывода относительно конца
- SEEK_HOLE
-
Устанавливает позицию ввода-вывода на следующую пустую область
- SEEK_SET
-
Устанавливает позицию ввода-вывода относительно начала
- WRITABLE
-
Маска события готовности к записи для
IO#wait.
Открытые методы класса
static VALUE
rb_io_s_binread(int argc, VALUE *argv, VALUE io)
{
VALUE offset;
struct foreach_arg arg;
enum rb_io_mode fmode = FMODE_READABLE|FMODE_BINMODE;
enum {
oflags = O_RDONLY
#ifdef O_BINARY
|O_BINARY
#endif
};
struct rb_io_encoding convconfig = {NULL, NULL, 0, Qnil};
rb_scan_args(argc, argv, "12", NULL, NULL, &offset);
FilePathValue(argv[0]);
convconfig.enc = rb_ascii8bit_encoding();
arg.io = rb_io_open_generic(io, argv[0], oflags, fmode, &convconfig, 0);
if (NIL_P(arg.io)) return Qnil;
arg.argv = argv+1;
arg.argc = (argc > 1) ? 1 : 0;
if (!NIL_P(offset)) {
struct seek_arg sarg;
int state = 0;
sarg.io = arg.io;
sarg.offset = offset;
sarg.mode = SEEK_SET;
rb_protect(seek_before_access, (VALUE)&sarg, &state);
if (state) {
rb_io_close(arg.io);
rb_jump_tag(state);
}
}
return rb_ensure(io_s_read, (VALUE)&arg, rb_io_close, arg.io);
} Работает как IO.read, но поток открывается в двоичном режиме с кодировкой ASCII-8BIT.
static VALUE
rb_io_s_binwrite(int argc, VALUE *argv, VALUE io)
{
return io_s_write(argc, argv, io, 1);
} Работает как IO.write, но поток открывается в двоичном режиме с кодировкой ASCII-8BIT.
static VALUE
console_dev(int argc, VALUE *argv, VALUE klass)
{
VALUE con = 0;
VALUE sym = 0;
if (argc) {
Check_Type(sym = argv[0], T_SYMBOL);
}
// Force the class to be File.
if (klass == rb_cIO) klass = rb_cFile;
if (console_dev_get(klass, &con)) {
if (!RB_TYPE_P(con, T_FILE) || RTEST(rb_io_closed_p(con))) {
console_dev_remove(klass);
con = 0;
}
}
if (sym) {
if (sym == ID2SYM(id_close) && argc == 1) {
if (con) {
rb_io_close(con);
console_dev_remove(klass);
con = 0;
}
return Qnil;
}
}
if (!con) {
#if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H || defined HAVE_SGTTY_H
# define CONSOLE_DEVICE "/dev/tty"
#elif defined _WIN32
# define CONSOLE_DEVICE "con$"
# define CONSOLE_DEVICE_FOR_READING "conin$"
# define CONSOLE_DEVICE_FOR_WRITING "conout$"
#endif
#ifndef CONSOLE_DEVICE_FOR_READING
# define CONSOLE_DEVICE_FOR_READING CONSOLE_DEVICE
#endif
#ifdef CONSOLE_DEVICE_FOR_WRITING
VALUE out;
#endif
int fd;
VALUE path = rb_obj_freeze(rb_str_new2(CONSOLE_DEVICE));
#ifdef CONSOLE_DEVICE_FOR_WRITING
fd = rb_cloexec_open(CONSOLE_DEVICE_FOR_WRITING, O_RDWR, 0);
if (fd < 0) return Qnil;
out = rb_io_open_descriptor(klass, fd, FMODE_WRITABLE | FMODE_SYNC, path, Qnil, NULL);
#endif
fd = rb_cloexec_open(CONSOLE_DEVICE_FOR_READING, O_RDWR, 0);
if (fd < 0) {
#ifdef CONSOLE_DEVICE_FOR_WRITING
rb_io_close(out);
#endif
return Qnil;
}
con = rb_io_open_descriptor(klass, fd, FMODE_READWRITE | FMODE_SYNC, path, Qnil, NULL);
#ifdef CONSOLE_DEVICE_FOR_WRITING
rb_io_set_write_io(con, out);
#endif
console_dev_set(klass, con);
}
if (sym) {
return rb_f_send(argc, argv, con);
}
return con;
} static VALUE
rb_io_s_copy_stream(int argc, VALUE *argv, VALUE io)
{
VALUE src, dst, length, src_offset;
struct copy_stream_struct st;
MEMZERO(&st, struct copy_stream_struct, 1);
rb_scan_args(argc, argv, "22", &src, &dst, &length, &src_offset);
st.src = src;
st.dst = dst;
st.src_fptr = NULL;
st.dst_fptr = NULL;
if (NIL_P(length))
st.copy_length = (rb_off_t)-1;
else
st.copy_length = NUM2OFFT(length);
if (NIL_P(src_offset))
st.src_offset = (rb_off_t)-1;
else
st.src_offset = NUM2OFFT(src_offset);
rb_ensure(copy_stream_body, (VALUE)&st, copy_stream_finalize, (VALUE)&st);
return OFFT2NUM(st.total);
} Копирует данные из указанного src в указанный dst и возвращает количество скопированных байтов.
-
Указанный
srcдолжен быть одним из следующих:-
Путь к доступному для чтения файлу, из которого будут считываться исходные данные.
-
Объект, подобный IO, открытый для чтения и способный отвечать на вызов метода
:readpartialили метода:read.
-
-
Указанный
dstдолжен быть одним из следующих:-
Путь к доступному для записи файлу, в который будут записываться данные.
-
Объект, подобный IO, открытый для записи и способный отвечать на вызов метода
:write.
-
В приведённых здесь примерах в качестве источника используется файл t.txt:
File.read('t.txt')
# => "First line\nSecond line\n\nThird line\nFourth line\n"
File.read('t.txt').size # => 47
Если заданы только аргументы src и dst, копируется весь исходный поток:
# Paths.
IO.copy_stream('t.txt', 't.tmp') # => 47
# IOs (recall that a File is also an IO).
src_io = File.open('t.txt', 'r') # => #<File:t.txt>
dst_io = File.open('t.tmp', 'w') # => #<File:t.tmp>
IO.copy_stream(src_io, dst_io) # => 47
src_io.close
dst_io.close
Если аргумент src_length — неотрицательное целое число, копируется не более указанного количества байтов:
IO.copy_stream('t.txt', 't.tmp', 10) # => 10
File.read('t.tmp') # => "First line"
Если также задан аргумент src_offset, чтение исходного потока начинается с указанного смещения:
IO.copy_stream('t.txt', 't.tmp', 11, 11) # => 11
IO.read('t.tmp') # => "Second line"
static VALUE
rb_io_s_for_fd(int argc, VALUE *argv, VALUE klass)
{
VALUE io = rb_obj_alloc(klass);
rb_io_initialize(argc, argv, io);
return io;
} Синоним IO.new.
static VALUE
rb_io_s_foreach(int argc, VALUE *argv, VALUE self)
{
VALUE opt;
int orig_argc = argc;
struct foreach_arg arg;
struct getline_arg garg;
argc = rb_scan_args(argc, argv, "12:", NULL, NULL, NULL, &opt);
RETURN_ENUMERATOR(self, orig_argc, argv);
extract_getline_args(argc-1, argv+1, &garg);
open_key_args(self, argc, argv, opt, &arg);
if (NIL_P(arg.io)) return Qnil;
extract_getline_opts(opt, &garg);
check_getline_args(&garg.rs, &garg.limit, garg.io = arg.io);
return rb_ensure(io_s_foreach, (VALUE)&garg, rb_io_close, arg.io);
} Вызывает блок для каждой следующей строки, считанной из потока.
Первый аргумент должен быть строкой, содержащей путь к файлу.
Если задан только аргумент path, строки разбираются из файла по указанному path с использованием разделителя строк по умолчанию, и блок вызывается для каждой следующей строки:
File.foreach('t.txt') {|line| p line }
Вывод: тот же, что и выше.
Для обеих форм — команды и пути — остальные аргументы одинаковы.
Если задан аргумент sep, строки разбираются с использованием указанного разделителя строк (см. Разделитель строк):
File.foreach('t.txt', 'li') {|line| p line }
Вывод:
"First li" "ne\nSecond li" "ne\n\nThird li" "ne\nFourth li" "ne\n"
Каждый абзац:
File.foreach('t.txt', '') {|paragraph| p paragraph }
Вывод:
"First line\nSecond line\n\n" "Third line\nFourth line\n"
Если задан аргумент limit, строки разбираются с использованием разделителя строк по умолчанию и указанного ограничения длины строки (см. Разделитель строк и Ограничение длины строки):
File.foreach('t.txt', 7) {|line| p line }
Вывод:
"First l" "ine\n" "Second " "line\n" "\n" "Third l" "ine\n" "Fourth l" "line\n"
Если заданы аргументы sep и limit, сочетаются оба описанных поведения (см. Разделитель строк и ограничение длины строки).
Необязательные ключевые аргументы opts задают:
Если блок не задан, возвращает Enumerator.
static VALUE
rb_io_initialize(int argc, VALUE *argv, VALUE io)
{
VALUE fnum, vmode;
VALUE opt;
rb_scan_args(argc, argv, "11:", &fnum, &vmode, &opt);
return io_initialize(io, fnum, vmode, opt);
} Создаёт и возвращает новый объект IO (файловый поток) из файлового дескриптора.
IO.new может быть полезен для взаимодействия с низкоуровневыми библиотеками. Для работы на более высоком уровне может быть проще создать файловый поток с помощью File.open.
Аргумент fd должен быть допустимым файловым дескриптором (целым числом):
path = 't.tmp' fd = IO.sysopen(path) # => 3 IO.new(fd) # => #<IO:fd 3>
Новый объект IO не наследует кодировку (поскольку у целочисленного файлового дескриптора нет кодировки):
fd = IO.sysopen('t.rus', 'rb')
io = IO.new(fd)
io.external_encoding # => #<Encoding:UTF-8> # Not ASCII-8BIT.
Необязательный аргумент mode (по умолчанию — ‘r’) должен задавать допустимый режим; см. Режимы доступа:
IO.new(fd, 'w') # => #<IO:fd 3> IO.new(fd, File::WRONLY) # => #<IO:fd 3>
Необязательные ключевые аргументы opts задают:
Примеры:
IO.new(fd, internal_encoding: nil) # => #<IO:fd 3> IO.new(fd, autoclose: true) # => #<IO:fd 3>
static VALUE
rb_io_s_open(int argc, VALUE *argv, VALUE klass)
{
VALUE io = rb_class_new_instance_kw(argc, argv, klass, RB_PASS_CALLED_KEYWORDS);
if (rb_block_given_p()) {
return rb_ensure(rb_yield, io, io_close, io);
}
return io;
} Создаёт новый объект IO с помощью IO.new, передавая ему указанные аргументы.
Если блок не задан, возвращает объект IO.
Если блок задан, вызывает его с объектом IO и возвращает значение блока.
static VALUE
rb_io_s_pipe(int argc, VALUE *argv, VALUE klass)
{
int pipes[2], state;
VALUE r, w, args[3], v1, v2;
VALUE opt;
rb_io_t *fptr, *fptr2;
struct io_encoding_set_args ies_args;
enum rb_io_mode fmode = 0;
VALUE ret;
argc = rb_scan_args(argc, argv, "02:", &v1, &v2, &opt);
if (rb_pipe(pipes) < 0)
rb_sys_fail(0);
args[0] = klass;
args[1] = INT2NUM(pipes[0]);
args[2] = INT2FIX(O_RDONLY);
r = rb_protect(io_new_instance, (VALUE)args, &state);
if (state) {
close(pipes[0]);
close(pipes[1]);
rb_jump_tag(state);
}
GetOpenFile(r, fptr);
ies_args.fptr = fptr;
ies_args.v1 = v1;
ies_args.v2 = v2;
ies_args.opt = opt;
rb_protect(io_encoding_set_v, (VALUE)&ies_args, &state);
if (state) {
close(pipes[1]);
io_close(r);
rb_jump_tag(state);
}
args[1] = INT2NUM(pipes[1]);
args[2] = INT2FIX(O_WRONLY);
w = rb_protect(io_new_instance, (VALUE)args, &state);
if (state) {
close(pipes[1]);
if (!NIL_P(r)) rb_io_close(r);
rb_jump_tag(state);
}
GetOpenFile(w, fptr2);
rb_io_synchronized(fptr2);
extract_binmode(opt, &fmode);
if ((fmode & FMODE_BINMODE) && NIL_P(v1)) {
rb_io_ascii8bit_binmode(r);
rb_io_ascii8bit_binmode(w);
}
#if DEFAULT_TEXTMODE
if ((fptr->mode & FMODE_TEXTMODE) && (fmode & FMODE_BINMODE)) {
fptr->mode &= ~FMODE_TEXTMODE;
setmode(fptr->fd, O_BINARY);
}
#if RUBY_CRLF_ENVIRONMENT
if (fptr->encs.ecflags & ECONV_DEFAULT_NEWLINE_DECORATOR) {
fptr->encs.ecflags |= ECONV_UNIVERSAL_NEWLINE_DECORATOR;
}
#endif
#endif
fptr->mode |= fmode;
#if DEFAULT_TEXTMODE
if ((fptr2->mode & FMODE_TEXTMODE) && (fmode & FMODE_BINMODE)) {
fptr2->mode &= ~FMODE_TEXTMODE;
setmode(fptr2->fd, O_BINARY);
}
#endif
fptr2->mode |= fmode;
ret = rb_assoc_new(r, w);
if (rb_block_given_p()) {
VALUE rw[2];
rw[0] = r;
rw[1] = w;
return rb_ensure(rb_yield, ret, pipe_pair_close, (VALUE)rw);
}
return ret;
} Создаёт соединённую пару концов канала: read_io и write_io.
Если задан аргумент enc_string, он должен быть строкой, содержащей одно из следующего:
-
Название кодировки, используемой как внешняя кодировка.
-
Названия двух кодировок, разделённые двоеточием, которые будут использоваться как внешняя и внутренняя кодировки.
Если задан аргумент int_enc, он должен быть объектом Encoding или строкой с названием кодировки, задающей используемую внутреннюю кодировку; если также задан аргумент ext_enc, он должен быть объектом Encoding или строкой с названием кодировки, задающей используемую внешнюю кодировку.
Строке, считанной из read_io, присваивается внешняя кодировка; если также задана внутренняя кодировка, строка преобразуется в неё и получает соответствующую метку кодировки.
Если задана какая-либо кодировка, необязательные аргументы-хеши указывают параметры преобразования.
Необязательные ключевые аргументы opts задают:
Если блок не задан, оба конца возвращаются в массиве:
IO.pipe # => [#<IO:fd 4>, #<IO:fd 5>]
Если блок задан, он вызывается с двумя концами; затем оба конца закрываются и возвращается значение блока:
IO.pipe {|read_io, write_io| p read_io; p write_io }
Вывод:
#<IO:fd 6> #<IO:fd 7>
Доступно не на всех платформах.
В приведённом ниже примере два процесса закрывают те концы канала, которые они не используют. Это не просто вопрос аккуратности. Конец канала для чтения не получит признак конца файла, пока остаётся хотя бы один процесс, записывающий в открытый канал. В случае родительского процесса rd.read никогда не вернёт результат, если сначала не выполнить wr.close:
rd, wr = IO.pipe
if fork
wr.close
puts "Parent got: <#{rd.read}>"
rd.close
Process.wait
else
rd.close
puts 'Sending message to parent'
wr.write "Hi Dad"
wr.close
end
результат:
Sending message to parent Parent got: <Hi Dad>
static VALUE
rb_io_s_popen(int argc, VALUE *argv, VALUE klass)
{
VALUE pname, pmode = Qnil, opt = Qnil, env = Qnil;
if (argc > 1 && !NIL_P(opt = rb_check_hash_type(argv[argc-1]))) --argc;
if (argc > 1 && !NIL_P(env = rb_check_hash_type(argv[0]))) --argc, ++argv;
switch (argc) {
case 2:
pmode = argv[1];
case 1:
pname = argv[0];
break;
default:
{
int ex = !NIL_P(opt);
rb_error_arity(argc + ex, 1 + ex, 2 + ex);
}
}
return popen_finish(rb_io_popen(pname, pmode, env, opt), klass);
} Выполняет указанную команду cmd как дочерний процесс, связав его $stdin и $stdout с новым потоком io.
При вызове с недоверенными данными этот метод может создавать уязвимости безопасности; см. Инъекция команд.
Если блок не задан, возвращается новый поток, который в зависимости от заданного mode может быть открыт для чтения, записи или обоих действий. Чтобы избежать утечек ресурсов, поток следует закрыть явным образом (в конечном итоге).
Если блок задан, поток передаётся ему (снова: поток может быть открыт для чтения, записи или обоих действий); при выходе из блока поток закрывается, возвращается значение блока, а глобальной переменной $? присваивается статус завершения дочернего процесса.
Необязательный аргумент mode может задавать любой допустимый режим IO. См. Режимы доступа.
Обязательный аргумент cmd определяет, что из перечисленного произойдёт:
-
Процесс создаёт ответвление.
-
Указанная программа запускается в оболочке.
-
Указанная программа запускается с заданными аргументами.
-
Указанная программа запускается с заданными аргументами и заданным
argv0.
Каждый из этих вариантов подробно описан ниже.
Необязательный аргумент-хеш env задаёт пары имя/значение, которые добавляются к переменным окружения дочернего процесса:
IO.popen({'FOO' => 'bar'}, 'ruby', 'r+') do |pipe|
pipe.puts 'puts ENV["FOO"]'
pipe.close_write
pipe.gets
end => "bar\n"
Необязательные ключевые аргументы opts задают:
-
Параметры для
Kernel#spawn.
Дочерний процесс с ответвлением
Если аргумент cmd — однобайтовая строка '-', процесс создаёт ответвление:
IO.popen('-') do |pipe|
if pipe
$stderr.puts "In parent, child pid is #{pipe.pid}\n"
else
$stderr.puts "In child, pid is #{$$}\n"
end
end
Вывод:
In parent, child pid is 26253 In child, pid is 26253
Обратите внимание, что это поддерживается не на всех платформах.
Дочерний процесс оболочки
Если аргумент cmd — одна строка (но не '-'), программа с именем cmd запускается как команда оболочки:
IO.popen('uname') do |pipe|
pipe.readlines
end
Вывод:
["Linux\n"]
Другой пример:
IO.popen('/bin/sh', 'r+') do |pipe|
pipe.puts('ls')
pipe.close_write
$stderr.puts pipe.readlines.size
end
Вывод:
213
Дочерний процесс программы
Если аргумент cmd — массив строк, программа с именем cmd[0] запускается со всеми элементами cmd в качестве аргументов:
IO.popen(['du', '..', '.']) do |pipe| $stderr.puts pipe.readlines.size end
Вывод:
1111
Дочерний процесс программы с argv0
Если аргумент cmd — массив, первый элемент которого представляет собой массив из двух строк, а остальные элементы (если есть) — строки:
-
cmd[0][0](первая строка во вложенном массиве) — имя запускаемой программы. -
cmd[0][1](вторая строка во вложенном массиве) задаётся какargv[0]программы. -
cmd[1..-1](строки во внешнем массиве) — аргументы программы.
Пример (задаёт $0 равным ‘foo’):
IO.popen([['/bin/sh', 'foo'], '-c', 'echo $0']).read # => "foo\n"
Некоторые особые примеры
# Set IO encoding.
IO.popen("nkf -e filename", :external_encoding=>"EUC-JP") {|nkf_io|
euc_jp_string = nkf_io.read
}
# Merge standard output and standard error using Kernel#spawn option. See Kernel#spawn.
IO.popen(["ls", "/", :err=>[:child, :out]]) do |io|
ls_result_with_error = io.read
end
# Use mixture of spawn options and IO options.
IO.popen(["ls", "/"], :err=>[:child, :out]) do |io|
ls_result_with_error = io.read
end
f = IO.popen("uname")
p f.readlines
f.close
puts "Parent is #{Process.pid}"
IO.popen("date") {|f| puts f.gets }
IO.popen("-") {|f| $stderr.puts "#{Process.pid} is here, f is #{f.inspect}"}
p $?
IO.popen(%w"sed -e s|^|<foo>| -e s&$&;zot;&", "r+") {|f|
f.puts "bar"; f.close_write; puts f.gets
}
Вывод (из последнего раздела):
["Linux\n"] Parent is 21346 Thu Jan 15 22:41:19 JST 2009 21346 is here, f is #<IO:fd 3> 21352 is here, f is nil #<Process::Status: pid 21352 exit 0> <foo>bar;zot;
Вызывает исключения, которые вызывают IO.pipe и Kernel.spawn.
static VALUE
rb_io_s_read(int argc, VALUE *argv, VALUE io)
{
VALUE opt, offset;
long off;
struct foreach_arg arg;
argc = rb_scan_args(argc, argv, "13:", NULL, NULL, &offset, NULL, &opt);
if (!NIL_P(offset) && (off = NUM2LONG(offset)) < 0) {
rb_raise(rb_eArgError, "negative offset %ld given", off);
}
open_key_args(io, argc, argv, opt, &arg);
if (NIL_P(arg.io)) return Qnil;
if (!NIL_P(offset)) {
struct seek_arg sarg;
int state = 0;
sarg.io = arg.io;
sarg.offset = offset;
sarg.mode = SEEK_SET;
rb_protect(seek_before_access, (VALUE)&sarg, &state);
if (state) {
rb_io_close(arg.io);
rb_jump_tag(state);
}
if (arg.argc == 2) arg.argc = 1;
}
return rb_ensure(io_s_read, (VALUE)&arg, rb_io_close, arg.io);
} Открывает поток, считывает и возвращает часть или всё его содержимое, а затем закрывает поток; если не было считано ни одного байта, возвращает nil.
Первый аргумент должен быть строкой, содержащей путь к файлу.
Если задан только аргумент path, файл по указанному пути считывается в текстовом режиме, а его содержимое возвращается целиком:
IO.read('t.txt')
# => "First line\nSecond line\n\nThird line\nFourth line\n"
В Windows текстовый режим может прекратить чтение и оставить часть байтов файла непрочитанными при обнаружении определённых специальных байтов. Если необходимо прочитать все байты файла, рассмотрите возможность использования IO.binread.
Если задан аргумент length, возвращает length байтов, если они доступны:
IO.read('t.txt', 7) # => "First l"
IO.read('t.txt', 700)
# => "First line\r\nSecond line\r\n\r\nFourth line\r\nFifth line\r\n"
Если заданы аргументы length и offset, возвращает length байтов, если они доступны, начиная с указанного offset:
IO.read('t.txt', 10, 2) # => "rst line\nS"
IO.read('t.txt', 10, 200) # => nil
Необязательные ключевые аргументы opts задают:
static VALUE
rb_io_s_readlines(int argc, VALUE *argv, VALUE io)
{
VALUE opt;
struct foreach_arg arg;
struct getline_arg garg;
argc = rb_scan_args(argc, argv, "12:", NULL, NULL, NULL, &opt);
extract_getline_args(argc-1, argv+1, &garg);
open_key_args(io, argc, argv, opt, &arg);
if (NIL_P(arg.io)) return Qnil;
extract_getline_opts(opt, &garg);
check_getline_args(&garg.rs, &garg.limit, garg.io = arg.io);
return rb_ensure(io_s_readlines, (VALUE)&garg, rb_io_close, arg.io);
} Возвращает массив всех строк, считанных из потока.
Первый аргумент должен быть строкой, содержащей путь к файлу.
Если задан только аргумент path, строки разбираются из файла по указанному path с использованием разделителя строк по умолчанию и возвращаются в виде массива:
IO.readlines('t.txt')
# => ["First line\n", "Second line\n", "\n", "Third line\n", "Fourth line\n"]
Если задан аргумент sep, строки разбираются с использованием указанного разделителя строк (см. Разделитель строк):
# Ordinary separator.
IO.readlines('t.txt', 'li')
# =>["First li", "ne\nSecond li", "ne\n\nThird li", "ne\nFourth li", "ne\n"]
# Get-paragraphs separator.
IO.readlines('t.txt', '')
# => ["First line\nSecond line\n\n", "Third line\nFourth line\n"]
# Get-all separator.
IO.readlines('t.txt', nil)
# => ["First line\nSecond line\n\nThird line\nFourth line\n"]
Если задан аргумент limit, строки разбираются с использованием разделителя строк по умолчанию и указанного ограничения длины строки (см. Разделитель строк и Ограничение длины строки):
IO.readlines('t.txt', 7)
# => ["First l", "ine\n", "Second ", "line\n", "\n", "Third l", "ine\n", "Fourth ", "line\n"]
Если заданы аргументы sep и limit, сочетаются оба описанных поведения (см. Разделитель строк и ограничение длины строки).
Необязательные ключевые аргументы opts задают:
static VALUE
rb_f_select(int argc, VALUE *argv, VALUE obj)
{
VALUE scheduler = rb_fiber_scheduler_current();
if (scheduler != Qnil) {
// It's optionally supported.
VALUE result = rb_fiber_scheduler_io_selectv(scheduler, argc, argv);
if (!UNDEF_P(result)) return result;
}
VALUE timeout;
struct select_args args;
struct timeval timerec;
int i;
rb_scan_args(argc, argv, "13", &args.read, &args.write, &args.except, &timeout);
if (NIL_P(timeout) || is_pos_inf(timeout)) {
args.timeout = 0;
}
else {
timerec = rb_time_interval(timeout);
args.timeout = &timerec;
}
for (i = 0; i < numberof(args.fdsets); ++i)
rb_fd_init(&args.fdsets[i]);
return rb_ensure(select_call, (VALUE)&args, select_end, (VALUE)&args);
} Вызывает системный вызов select(2), который отслеживает несколько файловых дескрипторов и ожидает, пока один или несколько из них не будут готовы к выполнению определённого типа операции ввода-вывода.
Реализовано не на всех платформах.
Каждый из аргументов read_ios, write_ios и error_ios представляет собой массив объектов IO.
Аргумент timeout — числовой интервал ожидания в секундах (например, целое число или число с плавающей точкой). timeout также может быть nil или Float::INFINITY. Значения nil и Float::INFINITY означают отсутствие тайм-аута.
Метод отслеживает объекты IO, переданные во все три массива, и ожидает готовности некоторых из них; он возвращает массив из трёх элементов:
-
Массив объектов из
read_ios, готовых к чтению. -
Массив объектов из
write_ios, готовых к записи. -
Массив объектов из
error_ios, для которых ожидают обработки исключения.
Если ни один объект не будет готов в течение заданного timeout, возвращается nil.
IO.select проверяет буфер объектов IO на наличие данных для чтения. Если буфер IO не пуст, IO.select сразу сообщает о готовности к чтению. Такая «проверка» выполняется только для объектов IO. Она не выполняется для объектов, подобных IO, например OpenSSL::SSL::SSLSocket.
Лучше всего вызывать IO.select после неблокирующих методов, таких как read_nonblock, write_nonblock и т. д. Эти методы вызывают исключение, расширенное модулем IO::WaitReadable или IO::WaitWritable. Эти модули указывают вызывающему коду, как ожидать события с помощью IO.select. Если вызывается IO::WaitReadable, следует ожидать готовности к чтению. Если вызывается IO::WaitWritable, следует ожидать готовности к записи.
Таким образом, блокирующее чтение (readpartial) можно эмулировать с помощью read_nonblock и IO.select следующим образом:
begin result = io_like.read_nonblock(maxlen) rescue IO::WaitReadable IO.select([io_like]) retry rescue IO::WaitWritable IO.select(nil, [io_like]) retry end
В частности, сочетание неблокирующих методов и IO.select предпочтительно для объектов, подобных IO, например OpenSSL::SSL::SSLSocket. У такого объекта есть метод to_io, возвращающий базовый объект IO. IO.select вызывает to_io, чтобы получить файловый дескриптор, готовности которого нужно дождаться.
Это означает, что уведомление IO.select о готовности к чтению не гарантирует готовность к чтению из объекта OpenSSL::SSL::SSLSocket.
Наиболее вероятная ситуация заключается в том, что OpenSSL::SSL::SSLSocket буферизует данные. IO.select не видит этот буфер. Поэтому IO.select может заблокироваться, даже если OpenSSL::SSL::SSLSocket#readpartial не блокируется.
Однако возможны и другие, более сложные ситуации.
SSL — это протокол, состоящий из последовательности записей. Запись состоит из нескольких байтов. Поэтому удалённая сторона SSL может отправить неполную запись: IO.select сообщит о готовности к чтению, но OpenSSL::SSL::SSLSocket не сможет расшифровать ни одного байта, и OpenSSL::SSL::SSLSocket#readpartial заблокируется.
Кроме того, удалённая сторона может запросить повторное согласование SSL, что заставляет локальный механизм SSL записать некоторые данные. Это означает, что OpenSSL::SSL::SSLSocket#readpartial может вызвать системный вызов write, который может заблокироваться. В такой ситуации OpenSSL::SSL::SSLSocket#read_nonblock вместо блокировки вызывает IO::WaitWritable. Поэтому, как и в приведённом выше примере, вызывающий код должен ожидать готовности к записи.
Сочетание неблокирующих методов и IO.select также полезно для потоков, таких как tty, канал или сокет, когда из одного потока читают несколько процессов.
Наконец, разработчики ядра Linux не гарантируют, что готовность к чтению, сообщённая select(2), означает готовность к чтению при последующем вызове read(2), даже если работает только один процесс; см. select(2)
Как правило, вызов IO.select перед IO#readpartial работает хорошо. Однако это не лучший способ использовать IO.select.
Уведомление select(2) о готовности к записи не указывает, сколько байтов можно записать. Метод IO#write блокируется, пока не будет записана вся заданная строка. Поэтому IO#write(two or more bytes) может заблокироваться после того, как IO.select сообщит о готовности к записи. Чтобы избежать блокировки, необходимо использовать IO#write_nonblock.
Блокирующую запись (write) можно эмулировать с помощью write_nonblock и IO.select следующим образом: при повторном согласовании SSL в OpenSSL::SSL::SSLSocket также следует перехватывать IO::WaitReadable.
while 0 < string.bytesize
begin
written = io_like.write_nonblock(string)
rescue IO::WaitReadable
IO.select([io_like])
retry
rescue IO::WaitWritable
IO.select(nil, [io_like])
retry
end
string = string.byteslice(written..-1)
end
Пример:
rp, wp = IO.pipe
mesg = "ping "
100.times {
# IO.select follows IO#read. Not the best way to use IO.select.
rs, ws, = IO.select([rp], [wp])
if r = rs[0]
ret = r.read(5)
print ret
case ret
when /ping/
mesg = "pong\n"
when /pong/
mesg = "ping "
end
end
if w = ws[0]
w.write(mesg)
end
}
Вывод:
ping pong ping pong ping pong (snipped) ping
static VALUE
rb_io_s_sysopen(int argc, VALUE *argv, VALUE _)
{
VALUE fname, vmode, vperm;
VALUE intmode;
int oflags, fd;
mode_t perm;
rb_scan_args(argc, argv, "12", &fname, &vmode, &vperm);
FilePathValue(fname);
if (NIL_P(vmode))
oflags = O_RDONLY;
else if (!NIL_P(intmode = rb_check_to_integer(vmode, "to_int")))
oflags = NUM2INT(intmode);
else {
StringValue(vmode);
oflags = rb_io_modestr_oflags(StringValueCStr(vmode));
}
if (NIL_P(vperm)) perm = 0666;
else perm = NUM2MODET(vperm);
RB_GC_GUARD(fname) = rb_str_new4(fname);
fd = rb_sysopen(fname, oflags, perm);
return INT2NUM(fd);
} Открывает файл по указанному пути в заданном режиме и с заданными разрешениями; возвращает целочисленный файловый дескриптор.
Если файл должен быть доступен для чтения, он должен существовать; если файл должен быть доступен для записи, но не существует, он создаётся с указанными разрешениями:
File.write('t.tmp', '') # => 0
IO.sysopen('t.tmp') # => 8
IO.sysopen('t.tmp', 'w') # => 9
static VALUE
rb_io_s_try_convert(VALUE dummy, VALUE io)
{
return rb_io_check_io(io);
} Пытается преобразовать object в объект IO с помощью метода to_io; при успехе возвращает новый объект IO, в противном случае — nil:
IO.try_convert(STDOUT) # => #<IO:<STDOUT>>
IO.try_convert(ARGF) # => #<IO:<STDIN>>
IO.try_convert('STDOUT') # => nil
static VALUE
rb_io_s_write(int argc, VALUE *argv, VALUE io)
{
return io_s_write(argc, argv, io, 0);
} Открывает поток, записывает в него указанные data и закрывает поток; возвращает количество записанных байтов.
Первый аргумент должен быть строкой, содержащей путь к файлу.
Если задан только аргумент path, в файл по указанному пути записываются заданные data:
IO.write('t.tmp', 'abc') # => 3
File.read('t.tmp') # => "abc"
Если offset равен нулю (значение по умолчанию), файл перезаписывается:
IO.write('t.tmp', 'A') # => 1
File.read('t.tmp') # => "A"
Если offset находится внутри содержимого файла, файл перезаписывается частично:
IO.write('t.tmp', 'abcdef') # => 3
File.read('t.tmp') # => "abcdef"
# Offset within content.
IO.write('t.tmp', '012', 2) # => 3
File.read('t.tmp') # => "ab012f"
Если offset находится за пределами содержимого файла, файл дополняется нулевыми символами "\u0000":
IO.write('t.tmp', 'xyz', 10) # => 3
File.read('t.tmp') # => "ab012f\u0000\u0000\u0000\u0000xyz"
Необязательные ключевые аргументы opts задают:
Открытые методы экземпляра
VALUE
rb_io_addstr(VALUE io, VALUE str)
{
rb_io_write(io, str);
return io;
} Записывает заданный object в self, который должен быть открыт для записи (см. Режимы доступа); возвращает self; если object не является строкой, он преобразуется с помощью метода to_s:
$stdout << 'Hello' << ', ' << 'World!' << "\n" $stdout << 'foo' << :bar << 2 << "\n"
Вывод:
Hello, World! foobar2
static VALUE
rb_io_advise(int argc, VALUE *argv, VALUE io)
{
VALUE advice, offset, len;
rb_off_t off, l;
rb_io_t *fptr;
rb_scan_args(argc, argv, "12", &advice, &offset, &len);
advice_arg_check(advice);
io = GetWriteIO(io);
GetOpenFile(io, fptr);
off = NIL_P(offset) ? 0 : NUM2OFFT(offset);
l = NIL_P(len) ? 0 : NUM2OFFT(len);
#ifdef HAVE_POSIX_FADVISE
return do_io_advise(fptr, advice, off, l);
#else
((void)off, (void)l); /* Ignore all hint */
return Qnil;
#endif
} Вызывает системный вызов Posix posix_fadvise(2), который сообщает о намерении обращаться к данным текущего файла определённым образом.
Аргументы и результаты зависят от платформы.
Область данных задаётся следующими параметрами:
-
offset: смещение первого байта данных. -
len: количество байтов для доступа; еслиlenравно нулю или превышает количество оставшихся байтов, будут обработаны все оставшиеся байты.
Аргумент advice — один из следующих символов:
-
:normal: приложение не даёт рекомендаций относительно шаблона доступа к указанным данным. Если для открытого файла рекомендации не заданы, используется это значение по умолчанию. -
:sequential: приложение предполагает, что доступ к указанным данным будет последовательным (сначала будут прочитаны данные с меньшими смещениями). -
:random: доступ к указанным данным будет осуществляться в случайном порядке. -
:noreuse: доступ к указанным данным будет осуществлён только один раз. -
:willneed: доступ к указанным данным потребуется в ближайшем будущем. -
:dontneed: доступ к указанным данным не потребуется в ближайшем будущем.
Реализовано не на всех платформах.
static VALUE
rb_io_set_autoclose(VALUE io, VALUE autoclose)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
if (!RTEST(autoclose))
fptr->mode |= FMODE_EXTERNAL;
else
fptr->mode &= ~FMODE_EXTERNAL;
return autoclose;
} Устанавливает флаг автоматического закрытия.
f = File.open(File::NULL) IO.for_fd(f.fileno).close f.gets # raises Errno::EBADF f = File.open(File::NULL) g = IO.for_fd(f.fileno) g.autoclose = false g.close f.gets # won't cause Errno::EBADF
static VALUE
rb_io_autoclose_p(VALUE io)
{
rb_io_t *fptr = RFILE(io)->fptr;
rb_io_check_closed(fptr);
return RBOOL(!(fptr->mode & FMODE_EXTERNAL));
} Возвращает true, если базовый файловый дескриптор ios будет закрыт при его финализации или при вызове close; в противном случае возвращает false.
static VALUE
console_beep(VALUE io)
{
#ifdef _WIN32
MessageBeep(0);
#else
int fd = GetWriteFD(io);
if (write(fd, "\a", 1) < 0) sys_fail(io);
#endif
return io;
} Издаёт звуковой сигнал в консоли вывода.
Чтобы использовать этот метод, необходимо подключить ‘io/console’.
static VALUE
rb_io_binmode_m(VALUE io)
{
VALUE write_io;
rb_io_ascii8bit_binmode(io);
write_io = GetWriteIO(io);
if (write_io != io)
rb_io_ascii8bit_binmode(write_io);
return io;
} Устанавливает для потока двоичный режим данных (см. Режим данных).
Режим данных потока нельзя изменить с двоичного на текстовый.
static VALUE
rb_io_binmode_p(VALUE io)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
return RBOOL(fptr->mode & FMODE_BINMODE);
} Возвращает true, если поток находится в двоичном режиме, и false в противном случае. См. Режим данных.
static VALUE
console_check_winsize_changed(VALUE io)
{
HANDLE h;
DWORD num;
h = (HANDLE)rb_w32_get_osfhandle(GetReadFD(io));
while (GetNumberOfConsoleInputEvents(h, &num) && num > 0) {
INPUT_RECORD rec;
if (ReadConsoleInput(h, &rec, 1, &num)) {
if (rec.EventType == WINDOW_BUFFER_SIZE_EVENT) {
rb_yield(Qnil);
}
}
}
return io;
} Выполняет блок, пока в очереди находятся события ввода с консоли.
Этот метод доступен только в Windows.
Чтобы использовать этот метод, необходимо подключить ‘io/console’.
static VALUE
console_clear_screen(VALUE io)
{
console_erase_screen(io, INT2FIX(2));
console_goto(io, INT2FIX(0), INT2FIX(0));
return io;
} Очищает весь экран и перемещает курсор в верхний левый угол.
Чтобы использовать этот метод, необходимо подключить ‘io/console’.
static VALUE
rb_io_close_m(VALUE io)
{
rb_io_t *fptr = rb_io_get_fptr(io);
if (fptr->fd < 0) {
return Qnil;
}
rb_io_close(io);
return Qnil;
} Закрывает поток для чтения и записи, если он открыт для одной или обеих операций; возвращает nil. См. Открытые и закрытые потоки.
Если поток открыт для записи, перед закрытием сбрасывает все буферизованные данные в операционную систему.
Если поток был открыт методом IO.popen, устанавливает глобальную переменную $? (статус завершения дочернего процесса).
Закрытие объекта IO, который уже закрыт, не является ошибкой. Метод просто возвращает nil.
Пример:
IO.popen('ruby', 'r+') do |pipe|
puts pipe.closed?
pipe.close
puts $?
puts pipe.closed?
end
Вывод:
false pid 13760 exit 0 true
Связанные методы: IO#close_read, IO#close_write, IO#closed?.
static VALUE
rb_io_set_close_on_exec(VALUE io, VALUE arg)
{
int flag = RTEST(arg) ? FD_CLOEXEC : 0;
rb_io_t *fptr;
VALUE write_io;
int fd, ret;
write_io = GetWriteIO(io);
if (io != write_io) {
GetOpenFile(write_io, fptr);
if (fptr && 0 <= (fd = fptr->fd)) {
if ((ret = fcntl(fptr->fd, F_GETFD)) == -1) rb_sys_fail_path(fptr->pathv);
if ((ret & FD_CLOEXEC) != flag) {
ret = (ret & ~FD_CLOEXEC) | flag;
ret = fcntl(fd, F_SETFD, ret);
if (ret != 0) rb_sys_fail_path(fptr->pathv);
}
}
}
GetOpenFile(io, fptr);
if (fptr && 0 <= (fd = fptr->fd)) {
if ((ret = fcntl(fd, F_GETFD)) == -1) rb_sys_fail_path(fptr->pathv);
if ((ret & FD_CLOEXEC) != flag) {
ret = (ret & ~FD_CLOEXEC) | flag;
ret = fcntl(fd, F_SETFD, ret);
if (ret != 0) rb_sys_fail_path(fptr->pathv);
}
}
return Qnil;
} Устанавливает флаг закрытия при exec.
f = File.open(File::NULL)
f.close_on_exec = true
system("cat", "/proc/self/fd/#{f.fileno}") # cat: /proc/self/fd/3: No such file or directory
f.closed? #=> false
Начиная с Ruby 2.0.0, Ruby по умолчанию устанавливает флаги закрытия при exec для всех файловых дескрипторов. Поэтому устанавливать их вручную не нужно. Кроме того, сброс флага закрытия при exec может привести к утечке файловых дескрипторов, если другой поток использует fork() и exec() (например, через метод system()). Если вам действительно нужно унаследовать файловый дескриптор дочерним процессом, используйте аргумент spawn(), например fd=>fd.
static VALUE
rb_io_close_on_exec_p(VALUE io)
{
rb_io_t *fptr;
VALUE write_io;
int fd, ret;
write_io = GetWriteIO(io);
if (io != write_io) {
GetOpenFile(write_io, fptr);
if (fptr && 0 <= (fd = fptr->fd)) {
if ((ret = fcntl(fd, F_GETFD)) == -1) rb_sys_fail_path(fptr->pathv);
if (!(ret & FD_CLOEXEC)) return Qfalse;
}
}
GetOpenFile(io, fptr);
if (fptr && 0 <= (fd = fptr->fd)) {
if ((ret = fcntl(fd, F_GETFD)) == -1) rb_sys_fail_path(fptr->pathv);
if (!(ret & FD_CLOEXEC)) return Qfalse;
}
return Qtrue;
} Возвращает true, если поток будет закрыт при exec, и false в противном случае:
f = File.open('t.txt')
f.close_on_exec? # => true
f.close_on_exec = false
f.close_on_exec? # => false
f.close
static VALUE
rb_io_close_read(VALUE io)
{
rb_io_t *fptr;
VALUE write_io;
fptr = rb_io_get_fptr(rb_io_taint_check(io));
if (fptr->fd < 0) return Qnil;
if (is_socket(fptr->fd, fptr->pathv)) {
#ifndef SHUT_RD
# define SHUT_RD 0
#endif
if (shutdown(fptr->fd, SHUT_RD) < 0)
rb_sys_fail_path(fptr->pathv);
fptr->mode &= ~FMODE_READABLE;
if (!(fptr->mode & FMODE_WRITABLE))
return rb_io_close(io);
return Qnil;
}
write_io = GetWriteIO(io);
if (io != write_io) {
rb_io_t *wfptr;
wfptr = rb_io_get_fptr(rb_io_taint_check(write_io));
wfptr->pid = fptr->pid;
fptr->pid = 0;
RFILE(io)->fptr = wfptr;
/* bind to write_io temporarily to get rid of memory/fd leak */
fptr->tied_io_for_writing = 0;
RFILE(write_io)->fptr = fptr;
rb_io_fptr_cleanup(fptr, FALSE);
/* should not finalize fptr because another thread may be reading it */
return Qnil;
}
if ((fptr->mode & (FMODE_DUPLEX|FMODE_WRITABLE)) == FMODE_WRITABLE) {
rb_raise(rb_eIOError, "closing non-duplex IO for reading");
}
return rb_io_close(io);
} Закрывает поток для чтения, если он открыт для чтения; возвращает nil. См. Открытые и закрытые потоки.
Если поток был открыт методом IO.popen и также закрыт для записи, устанавливает глобальную переменную $? (статус завершения дочернего процесса).
Пример:
IO.popen('ruby', 'r+') do |pipe|
puts pipe.closed?
pipe.close_write
puts pipe.closed?
pipe.close_read
puts $?
puts pipe.closed?
end
Вывод:
false false pid 14748 exit 0 true
Связанные методы: IO#close, IO#close_write, IO#closed?.
static VALUE
rb_io_close_write(VALUE io)
{
rb_io_t *fptr;
VALUE write_io;
write_io = GetWriteIO(io);
fptr = rb_io_get_fptr(rb_io_taint_check(write_io));
if (fptr->fd < 0) return Qnil;
if (is_socket(fptr->fd, fptr->pathv)) {
#ifndef SHUT_WR
# define SHUT_WR 1
#endif
if (shutdown(fptr->fd, SHUT_WR) < 0)
rb_sys_fail_path(fptr->pathv);
fptr->mode &= ~FMODE_WRITABLE;
if (!(fptr->mode & FMODE_READABLE))
return rb_io_close(write_io);
return Qnil;
}
if ((fptr->mode & (FMODE_DUPLEX|FMODE_READABLE)) == FMODE_READABLE) {
rb_raise(rb_eIOError, "closing non-duplex IO for writing");
}
if (io != write_io) {
fptr = rb_io_get_fptr(rb_io_taint_check(io));
fptr->tied_io_for_writing = 0;
}
rb_io_close(write_io);
return Qnil;
} Закрывает поток для записи, если он открыт для записи; возвращает nil. См. Открытые и закрытые потоки.
Перед закрытием сбрасывает все буферизованные данные в операционную систему.
Если поток был открыт методом IO.popen и также закрыт для чтения, устанавливает глобальную переменную $? (статус завершения дочернего процесса).
IO.popen('ruby', 'r+') do |pipe|
puts pipe.closed?
pipe.close_read
puts pipe.closed?
pipe.close_write
puts $?
puts pipe.closed?
end
Вывод:
false false pid 15044 exit 0 true
Связанные методы: IO#close, IO#close_read, IO#closed?.
VALUE
rb_io_closed_p(VALUE io)
{
rb_io_t *fptr;
VALUE write_io;
rb_io_t *write_fptr;
write_io = GetWriteIO(io);
if (io != write_io) {
write_fptr = RFILE(write_io)->fptr;
if (write_fptr && 0 <= write_fptr->fd) {
return Qfalse;
}
}
fptr = rb_io_get_fptr(io);
return RBOOL(0 > fptr->fd);
} Возвращает true, если поток закрыт и для чтения, и для записи, и false в противном случае. См. Открытые и закрытые потоки.
IO.popen('ruby', 'r+') do |pipe|
puts pipe.closed?
pipe.close_read
puts pipe.closed?
pipe.close_write
puts pipe.closed?
end
Вывод:
false false true
Связанные методы: IO#close_read, IO#close_write, IO#close.
static VALUE
console_conmode_get(VALUE io)
{
conmode t;
int fd = GetReadFD(io);
if (!getattr(fd, &t)) sys_fail(io);
return conmode_new(cConmode, &t);
} Возвращает данные, представляющие текущий режим консоли.
Чтобы использовать этот метод, необходимо подключить ‘io/console’.
static VALUE
console_conmode_set(VALUE io, VALUE mode)
{
conmode *t, r;
int fd = GetReadFD(io);
TypedData_Get_Struct(mode, conmode, &conmode_type, t);
r = *t;
if (!setattr(fd, &r)) sys_fail(io);
return mode;
} Устанавливает режим консоли в mode.
Чтобы использовать этот метод, необходимо подключить ‘io/console’.
static VALUE
console_cooked(VALUE io)
{
return ttymode(io, rb_yield, io, set_cookedmode, NULL);
} Выполняет блок self в каноническом режиме.
STDIN.cooked(&:gets)
прочитает и вернёт строку с отображением ввода и редактированием строки.
Чтобы использовать этот метод, необходимо подключить ‘io/console’.
static VALUE
console_set_cooked(VALUE io)
{
conmode t;
int fd = GetReadFD(io);
if (!getattr(fd, &t)) sys_fail(io);
set_cookedmode(&t, NULL);
if (!setattr(fd, &t)) sys_fail(io);
return io;
} Включает канонический режим.
Чтобы восстановить режим терминала, используйте io.cooked { … }.
Чтобы использовать этот метод, необходимо подключить ‘io/console’.
static VALUE
console_cursor_pos(VALUE io)
{
#ifdef _WIN32
rb_console_size_t ws;
int fd = GetWriteFD(io);
if (!GetConsoleScreenBufferInfo((HANDLE)rb_w32_get_osfhandle(fd), &ws)) {
rb_syserr_fail(LAST_ERROR, 0);
}
return rb_assoc_new(UINT2NUM(ws.dwCursorPosition.Y - ws.srWindow.Top), UINT2NUM(ws.dwCursorPosition.X));
#else
static const struct query_args query = {"\033[6n", 0};
VALUE resp = console_vt_response(0, 0, io, &query);
VALUE row, column, term;
unsigned int r, c;
if (!RB_TYPE_P(resp, T_ARRAY) || RARRAY_LEN(resp) != 3) return Qnil;
term = RARRAY_AREF(resp, 2);
if (!RB_TYPE_P(term, T_STRING) || RSTRING_LEN(term) != 1) return Qnil;
if (RSTRING_PTR(term)[0] != 'R') return Qnil;
row = RARRAY_AREF(resp, 0);
column = RARRAY_AREF(resp, 1);
rb_ary_resize(resp, 2);
r = NUM2UINT(row) - 1;
c = NUM2UINT(column) - 1;
RARRAY_ASET(resp, 0, INT2NUM(r));
RARRAY_ASET(resp, 1, INT2NUM(c));
return resp;
#endif
} Возвращает текущую позицию курсора в виде массива из двух целых чисел (строка, столбец)
io.cursor # => [3, 5]
Чтобы использовать этот метод, необходимо подключить ‘io/console’.
static VALUE
console_cursor_set(VALUE io, VALUE cpos)
{
cpos = rb_convert_type(cpos, T_ARRAY, "Array", "to_ary");
if (RARRAY_LEN(cpos) != 2) rb_raise(rb_eArgError, "expected 2D coordinate");
return console_goto(io, RARRAY_AREF(cpos, 0), RARRAY_AREF(cpos, 1));
} То же, что и io.goto(line, column)
См. IO#goto.
Чтобы использовать этот метод, необходимо подключить ‘io/console’.
static VALUE
console_cursor_down(VALUE io, VALUE val)
{
return console_move(io, +NUM2INT(val), 0);
} Перемещает курсор вниз на n строк.
Чтобы использовать этот метод, необходимо подключить ‘io/console’.
static VALUE
console_cursor_left(VALUE io, VALUE val)
{
return console_move(io, 0, -NUM2INT(val));
} Перемещает курсор влево на n столбцов.
Чтобы использовать этот метод, необходимо подключить ‘io/console’.
static VALUE
console_cursor_right(VALUE io, VALUE val)
{
return console_move(io, 0, +NUM2INT(val));
} Перемещает курсор вправо на n столбцов.
Чтобы использовать этот метод, необходимо подключить ‘io/console’.
static VALUE
console_cursor_up(VALUE io, VALUE val)
{
return console_move(io, -NUM2INT(val), 0);
} Перемещает курсор вверх на n строк.
Чтобы использовать этот метод, необходимо подключить ‘io/console’.
static VALUE
rb_io_each_line(int argc, VALUE *argv, VALUE io)
{
VALUE str;
struct getline_arg args;
RETURN_ENUMERATOR(io, argc, argv);
prepare_getline_args(argc, argv, &args, io);
if (args.limit == 0)
rb_raise(rb_eArgError, "invalid limit: 0 for each_line");
while (!NIL_P(str = rb_io_getline_1(args.rs, args.limit, args.chomp, io))) {
rb_yield(str);
}
return io;
} static VALUE
rb_io_each_byte(VALUE io)
{
rb_io_t *fptr;
RETURN_ENUMERATOR(io, 0, 0);
GetOpenFile(io, fptr);
do {
while (fptr->rbuf.len > 0) {
char *p = fptr->rbuf.ptr + fptr->rbuf.off++;
fptr->rbuf.len--;
rb_yield(INT2FIX(*p & 0xff));
rb_io_check_byte_readable(fptr);
errno = 0;
}
READ_CHECK(fptr);
} while (io_fillbuf(fptr) >= 0);
return io;
} Вызывает заданный блок для каждого байта (0..255) в потоке; возвращает self. См. Байтовый ввод-вывод.
f = File.new('t.rus')
a = []
f.each_byte {|b| a << b }
a # => [209, 130, 208, 181, 209, 129, 209, 130]
f.close
Если блок не задан, возвращает Enumerator.
Связанные методы: IO#each_char, IO#each_codepoint.
static VALUE
rb_io_each_char(VALUE io)
{
rb_io_t *fptr;
rb_encoding *enc;
VALUE c;
RETURN_ENUMERATOR(io, 0, 0);
GetOpenFile(io, fptr);
rb_io_check_char_readable(fptr);
enc = io_input_encoding(fptr);
READ_CHECK(fptr);
while (!NIL_P(c = io_getc(fptr, enc))) {
rb_yield(c);
}
return io;
} Вызывает заданный блок для каждого символа в потоке; возвращает self. См. Посимвольный ввод-вывод.
f = File.new('t.rus')
a = []
f.each_char {|c| a << c.ord }
a # => [1090, 1077, 1089, 1090]
f.close
Если блок не задан, возвращает Enumerator.
Связанные методы: IO#each_byte, IO#each_codepoint.
static VALUE
rb_io_each_codepoint(VALUE io)
{
rb_io_t *fptr;
rb_encoding *enc;
unsigned int c;
int r, n;
RETURN_ENUMERATOR(io, 0, 0);
GetOpenFile(io, fptr);
rb_io_check_char_readable(fptr);
READ_CHECK(fptr);
enc = io_read_encoding(fptr);
if (NEED_READCONV(fptr)) {
SET_BINARY_MODE(fptr);
r = 1; /* no invalid char yet */
for (;;) {
make_readconv(fptr, 0);
for (;;) {
if (fptr->cbuf.len) {
r = rb_enc_precise_mbclen(fptr->cbuf.ptr+fptr->cbuf.off,
fptr->cbuf.ptr+fptr->cbuf.off+fptr->cbuf.len,
enc);
if (!MBCLEN_NEEDMORE_P(r))
break;
if (fptr->cbuf.len == fptr->cbuf.capa) {
rb_raise(rb_eIOError, "too long character");
}
}
if (more_char(fptr) == MORE_CHAR_FINISHED) {
clear_readconv(fptr);
if (!MBCLEN_CHARFOUND_P(r)) {
goto invalid;
}
return io;
}
}
if (MBCLEN_INVALID_P(r)) {
goto invalid;
}
n = MBCLEN_CHARFOUND_LEN(r);
c = rb_enc_codepoint(fptr->cbuf.ptr+fptr->cbuf.off,
fptr->cbuf.ptr+fptr->cbuf.off+fptr->cbuf.len,
enc);
fptr->cbuf.off += n;
fptr->cbuf.len -= n;
rb_yield(UINT2NUM(c));
rb_io_check_char_readable(fptr);
}
}
NEED_NEWLINE_DECORATOR_ON_READ_CHECK(fptr);
while (io_fillbuf(fptr) >= 0) {
r = rb_enc_precise_mbclen(fptr->rbuf.ptr+fptr->rbuf.off,
fptr->rbuf.ptr+fptr->rbuf.off+fptr->rbuf.len, enc);
if (MBCLEN_CHARFOUND_P(r) &&
(n = MBCLEN_CHARFOUND_LEN(r)) <= fptr->rbuf.len) {
c = rb_enc_codepoint(fptr->rbuf.ptr+fptr->rbuf.off,
fptr->rbuf.ptr+fptr->rbuf.off+fptr->rbuf.len, enc);
fptr->rbuf.off += n;
fptr->rbuf.len -= n;
rb_yield(UINT2NUM(c));
}
else if (MBCLEN_INVALID_P(r)) {
goto invalid;
}
else if (MBCLEN_NEEDMORE_P(r)) {
char cbuf[8], *p = cbuf;
int more = MBCLEN_NEEDMORE_LEN(r);
if (more > numberof(cbuf)) goto invalid;
more += n = fptr->rbuf.len;
if (more > numberof(cbuf)) goto invalid;
while ((n = (int)read_buffered_data(p, more, fptr)) > 0 &&
(p += n, (more -= n) > 0)) {
if (io_fillbuf(fptr) < 0) goto invalid;
if ((n = fptr->rbuf.len) > more) n = more;
}
r = rb_enc_precise_mbclen(cbuf, p, enc);
if (!MBCLEN_CHARFOUND_P(r)) goto invalid;
c = rb_enc_codepoint(cbuf, p, enc);
rb_yield(UINT2NUM(c));
}
else {
continue;
}
rb_io_check_byte_readable(fptr);
}
return io;
invalid:
rb_raise(rb_eArgError, "invalid byte sequence in %s", rb_enc_name(enc));
UNREACHABLE_RETURN(Qundef);
} Вызывает заданный блок для каждой кодовой точки в потоке; возвращает self:
f = File.new('t.rus')
a = []
f.each_codepoint {|c| a << c }
a # => [1090, 1077, 1089, 1090]
f.close
Если блок не задан, возвращает Enumerator.
Связанные методы: IO#each_byte, IO#each_char.
Вызывает блок для каждой оставшейся строки, прочитанной из потока; возвращает self. Если поток уже достиг конца, ничего не делает. См. Построчный ввод-вывод.
Если аргументы не заданы, читает строки, используя в качестве разделителя строк $/:
f = File.new('t.txt')
f.each_line {|line| p line }
f.each_line {|line| fail 'Cannot happen' }
f.close
Вывод:
"First line\n" "Second line\n" "\n" "Fourth line\n" "Fifth line\n"
Если задан только строковый аргумент sep, читает строки, используя в качестве разделителя строк sep; см. Разделитель строк:
f = File.new('t.txt')
f.each_line('li') {|line| p line }
f.close
Вывод:
"First li" "ne\nSecond li" "ne\n\nFourth li" "ne\nFifth li" "ne\n"
Распознаются два специальных значения для sep:
f = File.new('t.txt')
# Get all into one string.
f.each_line(nil) {|line| p line }
f.close
Вывод:
"First line\nSecond line\n\nFourth line\nFifth line\n"
f.rewind
# Get paragraphs (up to two line separators).
f.each_line('') {|line| p line }
Вывод:
"First line\nSecond line\n\n" "Fourth line\nFifth line\n"
Если задан только целочисленный аргумент limit, ограничивает количество байтов в каждой строке; см. Ограничение длины строки:
f = File.new('t.txt')
f.each_line(8) {|line| p line }
f.close
Вывод:
"First li" "ne\n" "Second l" "ine\n" "\n" "Fourth l" "ine\n" "Fifth li" "ne\n"
Если заданы аргументы sep и limit, объединяет оба поведения (см. Разделитель строк и ограничение длины строки).
Необязательный именованный аргумент chomp задаёт, следует ли удалять разделители строк:
f = File.new('t.txt')
f.each_line(chomp: true) {|line| p line }
f.close
Вывод:
"First line" "Second line" "" "Fourth line" "Fifth line"
Если блок не задан, возвращает Enumerator.
static VALUE
console_set_echo(VALUE io, VALUE f)
{
conmode t;
int fd = GetReadFD(io);
if (!getattr(fd, &t)) sys_fail(io);
if (RTEST(f))
set_echo(&t, NULL);
else
set_noecho(&t, NULL);
if (!setattr(fd, &t)) sys_fail(io);
return io;
} Включает или отключает отображение вводимых символов. На некоторых платформах допустимы не все сочетания этих флагов и режимов raw/cooked.
Чтобы использовать этот метод, необходимо подключить ‘io/console’.
static VALUE
console_echo_p(VALUE io)
{
conmode t;
int fd = GetReadFD(io);
if (!getattr(fd, &t)) sys_fail(io);
return echo_p(&t) ? Qtrue : Qfalse;
} Возвращает true, если отображение вводимых символов включено.
Чтобы использовать этот метод, необходимо подключить ‘io/console’.
VALUE
rb_io_eof(VALUE io)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
rb_io_check_char_readable(fptr);
if (READ_CHAR_PENDING(fptr)) return Qfalse;
if (READ_DATA_PENDING(fptr)) return Qfalse;
READ_CHECK(fptr);
#if RUBY_CRLF_ENVIRONMENT
if (!NEED_READCONV(fptr) && NEED_NEWLINE_DECORATOR_ON_READ(fptr)) {
return RBOOL(eof(fptr->fd));
}
#endif
return RBOOL(io_fillbuf(fptr) < 0);
} Возвращает true, если поток находится в конце, и false в противном случае; см. Позиция:
f = File.open('t.txt')
f.eof # => false
f.seek(0, :END) # => 0
f.eof # => true
f.close
Вызывает исключение, если поток не открыт для чтения; см. Режим.
Если self — это поток, например канал или сокет, метод блокируется, пока другой конец не отправит данные или не закроет соединение:
r, w = IO.pipe
Thread.new { sleep 1; w.close }
r.eof? # => true # After 1-second wait.
r, w = IO.pipe
Thread.new { sleep 1; w.puts "a" }
r.eof? # => false # After 1-second wait.
r, w = IO.pipe
r.eof? # blocks forever
Обратите внимание, что этот метод считывает данные во входной байтовый буфер. Поэтому IO#sysread может работать не так, как вы ожидаете, при использовании вместе с IO#eof?, если предварительно не вызвать IO#rewind (который недоступен для некоторых потоков).
static VALUE
console_erase_line(VALUE io, VALUE val)
{
int mode = mode_in_range(val, 2, "line erase");
#ifdef _WIN32
HANDLE h;
rb_console_size_t ws;
COORD *pos = &ws.dwCursorPosition;
DWORD w;
h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io));
if (!GetConsoleScreenBufferInfo(h, &ws)) {
rb_syserr_fail(LAST_ERROR, 0);
}
w = winsize_col(&ws);
switch (mode) {
case 0: /* after cursor */
w -= pos->X;
break;
case 1: /* before *and* cursor */
w = pos->X + 1;
pos->X = 0;
break;
case 2: /* entire line */
pos->X = 0;
break;
}
constat_clear(h, ws.wAttributes, w, *pos);
return io;
#else
rb_io_write(io, rb_sprintf(CSI "%dK", mode));
#endif
return io;
} Стирает строку в позиции курсора в соответствии со значением mode. Значение mode может быть одним из следующих: 0: после курсора 1: до курсора включительно 2: вся строка
Чтобы использовать этот метод, необходимо подключить ‘io/console’.
static VALUE
console_erase_screen(VALUE io, VALUE val)
{
int mode = mode_in_range(val, 3, "screen erase");
#ifdef _WIN32
HANDLE h;
rb_console_size_t ws;
COORD *pos = &ws.dwCursorPosition;
DWORD w;
h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io));
if (!GetConsoleScreenBufferInfo(h, &ws)) {
rb_syserr_fail(LAST_ERROR, 0);
}
w = winsize_col(&ws);
switch (mode) {
case 0: /* erase after cursor */
w = (w * (ws.srWindow.Bottom - pos->Y + 1) - pos->X);
break;
case 1: /* erase before *and* cursor */
w = (w * (pos->Y - ws.srWindow.Top) + pos->X + 1);
pos->X = 0;
pos->Y = ws.srWindow.Top;
break;
case 2: /* erase entire screen */
w = (w * winsize_row(&ws));
pos->X = 0;
pos->Y = ws.srWindow.Top;
break;
case 3: /* erase entire screen */
w = (w * ws.dwSize.Y);
pos->X = 0;
pos->Y = 0;
break;
}
constat_clear(h, ws.wAttributes, w, *pos);
#else
rb_io_write(io, rb_sprintf(CSI "%dJ", mode));
#endif
return io;
} Стирает экран в позиции курсора в соответствии со значением mode. Значение mode может быть одним из следующих: 0: после курсора 1: до курсора включительно 2: весь экран
Чтобы использовать этот метод, необходимо подключить ‘io/console’.
# File ext/pty/lib/expect.rb, line 33
def expect(pat,timeout=9999999)
buf = ''.dup
case pat
when String
e_pat = Regexp.new(Regexp.quote(pat))
when Regexp
e_pat = pat
else
raise TypeError, "unsupported pattern class: #{pat.class}"
end
@unusedBuf ||= ''
while true
if not @unusedBuf.empty?
c = @unusedBuf.slice!(0)
elsif !IO.select([self],nil,nil,timeout) or eof? then
result = nil
@unusedBuf = buf
break
else
c = getc
end
buf << c
if $expect_verbose
STDOUT.print c
STDOUT.flush
end
if mat=e_pat.match(buf) then
result = [buf,*mat.captures]
break
end
end
if block_given? then
yield result
else
return result
end
nil
end Библиотека expect добавляет метод экземпляра IO#expect, похожий на расширение TCL expect.
Чтобы использовать этот метод, необходимо подключить expect:
require 'expect'
Читает данные из IO, пока заданный pattern не совпадёт или не истечёт timeout.
Метод возвращает массив, содержащий прочитанный буфер, за которым следуют совпадения. Если задан блок, результат передаётся ему, а метод возвращает nil.
При вызове без блока метод ожидает, пока из IO не будут получены входные данные, соответствующие заданному pattern, или пока не истечёт указанный тайм-аут. Если шаблон найден в IO, возвращается массив. Первый элемент массива — вся строка, полученная из IO до совпадения с шаблоном; за ней следуют элементы, указывающие, какая часть шаблона совпала с группами регулярного выражения.
Необязательный параметр тайм-аута задаёт в секундах общее время ожидания совпадения с шаблоном. Если время ожидания истекает или обнаружен конец потока, возвращается nil либо nil передаётся блоку. Однако буфер, накопленный во время сеанса ожидания, сохраняется для следующего вызова expect. Тайм-аут по умолчанию — 9999999 секунд.
static VALUE
rb_io_external_encoding(VALUE io)
{
rb_io_t *fptr = RFILE(rb_io_taint_check(io))->fptr;
if (fptr->encs.enc2) {
return rb_enc_from_encoding(fptr->encs.enc2);
}
if (fptr->mode & FMODE_WRITABLE) {
if (fptr->encs.enc)
return rb_enc_from_encoding(fptr->encs.enc);
return Qnil;
}
return rb_enc_from_encoding(io_read_encoding(fptr));
} static VALUE
rb_io_fcntl(int argc, VALUE *argv, VALUE io)
{
VALUE req, arg;
rb_scan_args(argc, argv, "11", &req, &arg);
return rb_fcntl(io, req, arg);
} Вызывает системный вызов Posix fcntl(2), который предоставляет механизм для отправки низкоуровневых команд управления потоком ввода-вывода, ориентированным на файл, или запроса его состояния. Аргументы и результаты зависят от платформы.
Если argument — число, его значение передаётся напрямую; если это строка, она интерпретируется как двоичная последовательность байтов. (Array#pack может быть удобным способом создания такой строки.)
Реализовано не на всех платформах.
static VALUE
rb_io_fdatasync(VALUE io)
{
rb_io_t *fptr;
io = GetWriteIO(io);
GetOpenFile(io, fptr);
if (io_fflush(fptr) < 0)
rb_sys_fail_on_write(fptr);
if ((int)rb_io_blocking_region(fptr, nogvl_fdatasync, fptr) == 0)
return INT2FIX(0);
/* fall back */
return rb_io_fsync(io);
} Немедленно записывает на диск все данные, буферизованные в потоке, с помощью системного вызова операционной системы: fdatasync(2), если он поддерживается, в противном случае — с помощью fsync(2), если он поддерживается; иначе вызывает исключение.
static VALUE
rb_io_fileno(VALUE io)
{
rb_io_t *fptr = RFILE(io)->fptr;
int fd;
rb_io_check_closed(fptr);
fd = fptr->fd;
return INT2FIX(fd);
} Возвращает целочисленный файловый дескриптор потока:
$stdin.fileno # => 0
$stdout.fileno # => 1
$stderr.fileno # => 2
File.open('t.txt').fileno # => 10
f.close
VALUE
rb_io_flush(VALUE io)
{
return rb_io_flush_raw(io, 1);
} Сбрасывает данные, буферизованные в self, в операционную систему (но это не обязательно сбрасывает данные, буферизованные в самой операционной системе):
$stdout.print 'no newline' # Not necessarily flushed. $stdout.flush # Flushed.
static VALUE
rb_io_fsync(VALUE io)
{
rb_io_t *fptr;
io = GetWriteIO(io);
GetOpenFile(io, fptr);
if (io_fflush(fptr) < 0)
rb_sys_fail_on_write(fptr);
if ((int)rb_io_blocking_region(fptr, nogvl_fsync, fptr))
rb_sys_fail_path(fptr->pathv);
return INT2FIX(0);
} Немедленно записывает на диск все данные, буферизованные в потоке, с помощью системного вызова операционной системы fsync(2).
Обратите внимание на это различие:
-
IO#sync=: гарантирует сброс данных из внутренних буферов потока, но не гарантирует, что операционная система действительно запишет данные на диск. -
IO#fsync: гарантирует как сброс данных из внутренних буферов, так и запись данных на диск.
Вызывает исключение, если операционная система не поддерживает fsync(2).
VALUE
rb_io_getbyte(VALUE io)
{
rb_io_t *fptr;
int c;
GetOpenFile(io, fptr);
rb_io_check_byte_readable(fptr);
READ_CHECK(fptr);
VALUE r_stdout = rb_ractor_stdout();
if (fptr->fd == 0 && (fptr->mode & FMODE_TTY) && RB_TYPE_P(r_stdout, T_FILE)) {
rb_io_t *ofp;
GetOpenFile(r_stdout, ofp);
if (ofp->mode & FMODE_TTY) {
rb_io_flush(r_stdout);
}
}
if (io_fillbuf(fptr) < 0) {
return Qnil;
}
fptr->rbuf.off++;
fptr->rbuf.len--;
c = (unsigned char)fptr->rbuf.ptr[fptr->rbuf.off-1];
return INT2FIX(c & 0xff);
} Читает и возвращает следующий байт (в диапазоне 0..255) из потока; возвращает nil, если достигнут конец потока. См. Байтовый ввод-вывод.
f = File.open('t.txt')
f.getbyte # => 70
f.close
f = File.open('t.rus')
f.getbyte # => 209
f.close
Связанный метод: IO#readbyte (может вызвать EOFError).
static VALUE
rb_io_getc(VALUE io)
{
rb_io_t *fptr;
rb_encoding *enc;
GetOpenFile(io, fptr);
rb_io_check_char_readable(fptr);
enc = io_input_encoding(fptr);
READ_CHECK(fptr);
return io_getc(fptr, enc);
} Читает и возвращает следующую строку длиной в один символ из потока; возвращает nil, если достигнут конец потока. См. Символьный ввод-вывод.
f = File.open('t.txt')
f.getc # => "F"
f.close
f = File.open('t.rus')
f.getc.ord # => 1090
f.close
Связанный метод: IO#readchar (может вызвать EOFError).
static VALUE
console_getch(int argc, VALUE *argv, VALUE io)
{
rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts);
#ifndef _WIN32
return ttymode(io, getc_call, io, set_rawmode, optp);
#else
rb_io_t *fptr;
VALUE str;
wint_t c;
int len;
char buf[8];
wint_t wbuf[2];
# ifndef HAVE_RB_IO_WAIT
struct timeval *to = NULL, tv;
# else
VALUE timeout = Qnil;
# endif
GetOpenFile(io, fptr);
if (optp) {
if (optp->vtime) {
# ifndef HAVE_RB_IO_WAIT
to = &tv;
# else
struct timeval tv;
# endif
tv.tv_sec = optp->vtime / 10;
tv.tv_usec = (optp->vtime % 10) * 100000;
# ifdef HAVE_RB_IO_WAIT
timeout = rb_fiber_scheduler_make_timeout(&tv);
# endif
}
switch (optp->vmin) {
case 1: /* default */
break;
case 0: /* return nil when timed out */
if (optp->vtime) break;
/* fallthru */
default:
rb_warning("min option larger than 1 ignored");
}
if (optp->intr) {
# ifndef HAVE_RB_IO_WAIT
int w = rb_wait_for_single_fd(fptr->fd, RB_WAITFD_IN, to);
if (w < 0) rb_eof_error();
if (!(w & RB_WAITFD_IN)) return Qnil;
# else
VALUE result = rb_io_wait(io, RB_INT2NUM(RUBY_IO_READABLE), timeout);
if (!RTEST(result)) return Qnil;
# endif
}
else if (optp->vtime) {
rb_warning("Non-zero vtime option ignored if intr flag is unset");
}
}
len = (int)(VALUE)rb_thread_call_without_gvl(nogvl_getch, wbuf, RUBY_UBF_IO, 0);
switch (len) {
case 0:
return Qnil;
case 2:
buf[0] = (char)wbuf[0];
c = wbuf[1];
len = 1;
do {
buf[len++] = (unsigned char)c;
} while ((c >>= CHAR_BIT) && len < (int)sizeof(buf));
return rb_str_new(buf, len);
default:
c = wbuf[0];
len = rb_uv_to_utf8(buf, c);
str = rb_utf8_str_new(buf, len);
return rb_str_conv_enc(str, NULL, rb_default_external_encoding());
}
#endif
} Читает и возвращает символ в необработанном режиме.
Описание параметров см. в разделе IO#raw.
Для использования этого метода необходимо подключить ‘io/console’.
static VALUE
console_getpass(int argc, VALUE *argv, VALUE io)
{
VALUE str, wio;
rb_check_arity(argc, 0, 1);
wio = rb_io_get_write_io(io);
if (wio == io && io == rb_stdin) wio = rb_stderr;
prompt(argc, argv, wio);
rb_io_flush(wio);
str = rb_ensure(getpass_call, io, puts_call, wio);
return str_chomp(str);
} Читает и возвращает строку без отображения вводимых символов. Выводит prompt, если это не nil.
Символ новой строки, завершающий прочитанную строку, удаляется из возвращаемой строки; см. String#chomp!.
Для использования этого метода необходимо подключить ‘io/console’.
require 'io/console'
IO::console.getpass("Enter password:")
Enter password:
# => "mypassword"
static VALUE
rb_io_gets_m(int argc, VALUE *argv, VALUE io)
{
VALUE str;
str = rb_io_getline(argc, argv, io);
rb_lastline_set(str);
return str;
} Читает и возвращает строку из потока; присваивает возвращаемое значение переменной $_. См. Построчный ввод-вывод.
Если аргументы не указаны, возвращает следующую строку, определяемую разделителем строк $/, или nil, если строки больше нет:
f = File.open('t.txt')
f.gets # => "First line\n"
$_ # => "First line\n"
f.gets # => "\n"
f.gets # => "Fourth line\n"
f.gets # => "Fifth line\n"
f.gets # => nil
f.close
Если указан только строковый аргумент sep, возвращает следующую строку, определяемую разделителем строк sep, или nil, если строки больше нет; см. Разделитель строк:
f = File.new('t.txt')
f.gets('l') # => "First l"
f.gets('li') # => "ine\nSecond li"
f.gets('lin') # => "ne\n\nFourth lin"
f.gets # => "e\n"
f.close
Учитываются два специальных значения sep:
f = File.new('t.txt')
# Get all.
f.gets(nil) # => "First line\nSecond line\n\nFourth line\nFifth line\n"
f.rewind
# Get paragraph (up to two line separators).
f.gets('') # => "First line\nSecond line\n\n"
f.close
Если указан только целочисленный аргумент limit, ограничивает количество байтов в строке; см. Ограничение длины строки:
# No more than one line.
File.open('t.txt') {|f| f.gets(10) } # => "First line"
File.open('t.txt') {|f| f.gets(11) } # => "First line\n"
File.open('t.txt') {|f| f.gets(12) } # => "First line\n"
При указании аргументов sep и limit объединяются оба поведения (см. Разделитель строк и ограничение длины строки).
Необязательный именованный аргумент chomp задаёт, следует ли опускать разделители строк:
f = File.open('t.txt')
# Chomp the lines.
f.gets(chomp: true) # => "First line"
f.gets(chomp: true) # => "Second line"
f.gets(chomp: true) # => ""
f.gets(chomp: true) # => "Fourth line"
f.gets(chomp: true) # => "Fifth line"
f.gets(chomp: true) # => nil
f.close
static VALUE
console_goto(VALUE io, VALUE y, VALUE x)
{
#ifdef _WIN32
HANDLE h;
rb_console_size_t ws;
COORD *pos = &ws.dwCursorPosition;
h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io));
if (!GetConsoleScreenBufferInfo(h, &ws)) {
rb_syserr_fail(LAST_ERROR, 0);
}
pos->X = NUM2UINT(x);
pos->Y = ws.srWindow.Top + NUM2UINT(y);
if (!SetConsoleCursorPosition(h, *pos)) {
rb_syserr_fail(LAST_ERROR, 0);
}
#else
rb_io_write(io, rb_sprintf(CSI "%d;%dH", NUM2UINT(y)+1, NUM2UINT(x)+1));
#endif
return io;
} Устанавливает положение курсора на line и column.
Для использования этого метода необходимо подключить ‘io/console’.
static VALUE
console_goto_column(VALUE io, VALUE val)
{
#ifdef _WIN32
HANDLE h;
rb_console_size_t ws;
COORD *pos = &ws.dwCursorPosition;
h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io));
if (!GetConsoleScreenBufferInfo(h, &ws)) {
rb_syserr_fail(LAST_ERROR, 0);
}
pos->X = NUM2INT(val);
if (!SetConsoleCursorPosition(h, *pos)) {
rb_syserr_fail(LAST_ERROR, 0);
}
#else
rb_io_write(io, rb_sprintf(CSI "%dG", NUM2UINT(val)+1));
#endif
return io;
} Устанавливает положение курсора на column в той же строке, что и текущее положение.
Для использования этого метода необходимо подключить ‘io/console’.
static VALUE
console_iflush(VALUE io)
{
#if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
int fd = GetReadFD(io);
if (tcflush(fd, TCIFLUSH)) sys_fail(io);
#endif
return io;
} Сбрасывает входной буфер ядра.
Для использования этого метода необходимо подключить ‘io/console’.
static VALUE
rb_io_inspect(VALUE obj)
{
rb_io_t *fptr;
VALUE result;
static const char closed[] = " (closed)";
fptr = RFILE(obj)->fptr;
if (!fptr) return rb_any_to_s(obj);
result = rb_str_new_cstr("#<");
rb_str_append(result, rb_class_name(CLASS_OF(obj)));
rb_str_cat2(result, ":");
if (NIL_P(fptr->pathv)) {
if (fptr->fd < 0) {
rb_str_cat(result, closed+1, strlen(closed)-1);
}
else {
rb_str_catf(result, "fd %d", fptr->fd);
}
}
else {
rb_str_append(result, fptr->pathv);
if (fptr->fd < 0) {
rb_str_cat(result, closed, strlen(closed));
}
}
return rb_str_cat2(result, ">");
} Возвращает строковое представление self:
f = File.open('t.txt')
f.inspect # => "#<File:t.txt>"
f.close
static VALUE
rb_io_internal_encoding(VALUE io)
{
rb_io_t *fptr = RFILE(rb_io_taint_check(io))->fptr;
if (!fptr->encs.enc2) return Qnil;
return rb_enc_from_encoding(io_read_encoding(fptr));
} static VALUE
rb_io_ioctl(int argc, VALUE *argv, VALUE io)
{
VALUE req, arg;
rb_scan_args(argc, argv, "11", &req, &arg);
return rb_ioctl(io, req, arg);
} Вызывает системный вызов Posix ioctl(2), который отправляет низкоуровневую команду устройству ввода-вывода.
Отправляет низкоуровневую команду устройству ввода-вывода. Аргументы и возвращаемое значение зависят от платформы. Эффект вызова зависит от платформы.
Если аргумент argument — целое число, оно передаётся напрямую; если это строка, она интерпретируется как двоичная последовательность байтов.
Реализовано не на всех платформах.
static VALUE
console_ioflush(VALUE io)
{
#if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
int fd1 = GetReadFD(io);
int fd2 = GetWriteFD(io);
if (fd2 != -1 && fd1 != fd2) {
if (tcflush(fd1, TCIFLUSH)) sys_fail(io);
if (tcflush(fd2, TCOFLUSH)) sys_fail(io);
}
else {
if (tcflush(fd1, TCIOFLUSH)) sys_fail(io);
}
#endif
return io;
} Сбрасывает входной и выходной буферы ядра.
Для использования этого метода необходимо подключить ‘io/console’.
static VALUE
rb_io_isatty(VALUE io)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
return RBOOL(isatty(fptr->fd) != 0);
} Возвращает true, если поток связан с терминальным устройством (tty), и false в противном случае:
f = File.new('t.txt').isatty #=> false
f.close
f = File.new('/dev/tty').isatty #=> true
f.close
static VALUE
rb_io_lineno(VALUE io)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
rb_io_check_char_readable(fptr);
return INT2NUM(fptr->lineno);
} Возвращает текущий номер строки для потока; см. Номер строки.
static VALUE
rb_io_set_lineno(VALUE io, VALUE lineno)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
rb_io_check_char_readable(fptr);
fptr->lineno = NUM2INT(lineno);
return lineno;
} Устанавливает и возвращает номер строки для потока; см. Номер строки.
static VALUE
console_noecho(VALUE io)
{
return ttymode(io, rb_yield, io, set_noecho, NULL);
} Передаёт self блоку, отключив отображение вводимых символов.
STDIN.noecho(&:gets)
прочитает и вернёт строку без отображения вводимых символов.
Для использования этого метода необходимо подключить ‘io/console’.
static VALUE
rb_io_nonblock_block(int argc, VALUE *argv, VALUE self)
{
int nb = 1;
int descriptor = rb_io_descriptor(self);
if (argc > 0) {
VALUE v;
rb_scan_args(argc, argv, "01", &v);
nb = RTEST(v);
}
int current_flags = get_fcntl_flags(descriptor);
int restore[2] = {descriptor, current_flags};
if (!io_nonblock_set(descriptor, current_flags, nb))
return rb_yield(self);
return rb_ensure(rb_yield, self, io_nonblock_restore, (VALUE)restore);
} Передаёт self блоку в неблокирующем режиме.
Если аргументом передано false, self передаётся блоку в блокирующем режиме. После выполнения блока восстанавливается исходный режим.
static VALUE
rb_io_nonblock_set(VALUE self, VALUE value)
{
if (RTEST(value)) {
rb_io_t *fptr;
GetOpenFile(self, fptr);
rb_io_set_nonblock(fptr);
}
else {
int descriptor = rb_io_descriptor(self);
io_nonblock_set(descriptor, get_fcntl_flags(descriptor), RTEST(value));
}
return self;
} Включает неблокирующий режим потока, если задано значение true, и блокирующий режим, если задано false.
Этот метод устанавливает или сбрасывает флаг O_NONBLOCK файлового дескриптора в ios.
Этот флаг не влияет на поведение большинства методов IO, поскольку они повторяют системные вызовы для завершения своей работы после EAGAIN и частичного чтения/записи. (Исключение — IO#syswrite, который не выполняет повторных попыток.)
Этот метод можно использовать для отключения неблокирующего режима стандартного ввода-вывода. Поскольку неблокирующие методы (read_nonblock и другие) включают неблокирующий режим, но не отключают его, этот метод можно использовать следующим образом.
END { STDOUT.nonblock = false }
STDOUT.write_nonblock("foo")
Поскольку флаг совместно используется процессами, а многие команды, не написанные на Ruby, не ожидают, что стандартный ввод-вывод будет работать в неблокирующем режиме, безопаснее сбросить флаг перед завершением программы Ruby.
Например, следующая программа Ruby оставляет STDIN/STDOUT/STDERR в неблокирующем режиме. (STDIN, STDOUT и STDERR связаны с терминалом. Поэтому перевод одного из них в неблокирующий режим влияет и на два остальных.) В результате команда cat пытается читать данные из стандартного ввода и выдаёт ошибку «Ресурс временно недоступен» (EAGAIN).
% ruby -e '
STDOUT.write_nonblock("foo\n")'; cat
foo
cat: -: Resource temporarily unavailable Сброс флага возвращает команде cat обычное поведение. (Команда cat ожидает ввод из стандартного ввода.)
% ruby -rio/nonblock -e '
END { STDOUT.nonblock = false }
STDOUT.write_nonblock("foo")
'; cat
foo static VALUE
rb_io_nonblock_p(VALUE io)
{
if (get_fcntl_flags(rb_io_descriptor(io)) & O_NONBLOCK)
return Qtrue;
return Qfalse;
} Возвращает true, если объект IO находится в неблокирующем режиме.
static VALUE
console_oflush(VALUE io)
{
int fd = GetWriteFD(io);
#if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
if (tcflush(fd, TCOFLUSH)) sys_fail(io);
#endif
(void)fd;
return io;
} Сбрасывает выходной буфер ядра.
Для использования этого метода необходимо подключить ‘io/console’.
VALUE
rb_io_path(VALUE io)
{
rb_io_t *fptr = RFILE(io)->fptr;
if (!fptr)
return Qnil;
return rb_obj_dup(fptr->pathv);
} Возвращает путь, связанный с IO, или nil, если с IO не связан путь. Наличие этого пути в файловой системе не гарантируется.
$stdin.path # => "<STDIN>"
File.open("testfile") {|f| f.path} # => "testfile"
static VALUE
io_pathconf(VALUE io, VALUE arg)
{
int name;
long ret;
name = NUM2INT(arg);
errno = 0;
ret = fpathconf(rb_io_descriptor(io), name);
if (ret == -1) {
if (errno == 0) /* no limit */
return Qnil;
rb_sys_fail("fpathconf");
}
return LONG2NUM(ret);
} Возвращает переменную конфигурации пути с помощью fpathconf().
name должен быть константой из Etc, начинающейся с PC_.
Возвращаемое значение — целое число или nil. nil означает отсутствие определённого предела. (fpathconf() возвращает -1, но errno не устанавливается.)
require 'etc'
IO.pipe {|r, w|
p w.pathconf(Etc::PC_PIPE_BUF) #=> 4096
}
static VALUE
rb_io_pid(VALUE io)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
if (!fptr->pid)
return Qnil;
return PIDT2NUM(fptr->pid);
} Возвращает идентификатор процесса дочернего процесса, связанного с потоком; он устанавливается методом IO#popen. Возвращает nil, если поток создан не методом IO#popen:
pipe = IO.popen("-")
if pipe
$stderr.puts "In parent, child pid is #{pipe.pid}"
else
$stderr.puts "In child, pid is #{$$}"
end
Вывод:
In child, pid is 26209 In parent, child pid is 26209
static VALUE
rb_io_set_pos(VALUE io, VALUE offset)
{
rb_io_t *fptr;
rb_off_t pos;
pos = NUM2OFFT(offset);
GetOpenFile(io, fptr);
pos = io_seek(fptr, pos, SEEK_SET);
if (pos < 0 && errno) rb_sys_fail_path(fptr->pathv);
return OFFT2NUM(pos);
} static VALUE
rb_io_pread(int argc, VALUE *argv, VALUE io)
{
VALUE len, offset, str;
rb_io_t *fptr;
ssize_t n;
struct prdwr_internal_arg arg;
int shrinkable;
rb_scan_args(argc, argv, "21", &len, &offset, &str);
arg.count = NUM2SIZET(len);
arg.offset = NUM2OFFT(offset);
shrinkable = io_setstrbuf(&str, (long)arg.count);
if (arg.count == 0) return str;
arg.buf = RSTRING_PTR(str);
GetOpenFile(io, fptr);
rb_io_check_byte_readable(fptr);
arg.io = fptr;
arg.fd = fptr->fd;
rb_io_check_closed(fptr);
rb_str_locktmp(str);
n = (ssize_t)rb_ensure(pread_internal_call, (VALUE)&arg, rb_str_unlocktmp, str);
if (n < 0) {
rb_sys_fail_path(fptr->pathv);
}
io_set_read_length(str, n, shrinkable);
if (n == 0 && arg.count > 0) {
rb_eof_error();
}
return str;
} Работает как IO#readpartial, за исключением того, что метод:
-
Читает данные по заданному
offset(в байтах). -
Не учитывает и не изменяет позицию потока (см. Позиция).
-
Обходит буферизацию потока в пользовательском пространстве.
Поскольку этот метод не изменяет состояние потока (в частности, его позицию), pread позволяет нескольким потокам и процессам использовать один и тот же объект IO для чтения данных со смещениями.
f = File.open('t.txt')
f.read # => "First line\nSecond line\n\nFourth line\nFifth line\n"
f.pos # => 52
# Read 12 bytes at offset 0.
f.pread(12, 0) # => "First line\n"
# Read 9 bytes at offset 8.
f.pread(9, 8) # => "ne\nSecon"
f.close
Недоступен на некоторых платформах.
static VALUE
console_key_pressed_p(VALUE io, VALUE k)
{
int vk = -1;
if (FIXNUM_P(k)) {
vk = NUM2UINT(k);
}
else {
const struct vktable *t;
const char *kn;
if (SYMBOL_P(k)) {
k = rb_sym2str(k);
kn = RSTRING_PTR(k);
}
else {
kn = StringValuePtr(k);
}
t = console_win32_vk(kn, RSTRING_LEN(k));
if (!t || (vk = (short)t->vk) == -1) {
rb_raise(rb_eArgError, "unknown virtual key code: % "PRIsVALUE, k);
}
}
return GetKeyState(vk) & 0x80 ? Qtrue : Qfalse;
} VALUE
rb_io_print(int argc, const VALUE *argv, VALUE out)
{
int i;
VALUE line;
/* if no argument given, print `$_' */
if (argc == 0) {
argc = 1;
line = rb_lastline_get();
argv = &line;
}
if (argc > 1 && !NIL_P(rb_output_fs)) {
rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "$, is set to non-nil value");
}
for (i=0; i<argc; i++) {
if (!NIL_P(rb_output_fs) && i>0) {
rb_io_write(out, rb_output_fs);
}
rb_io_write(out, argv[i]);
}
if (argc > 0 && !NIL_P(rb_output_rs)) {
rb_io_write(out, rb_output_rs);
}
return Qnil;
} Записывает переданные объекты в поток; возвращает nil. Добавляет разделитель записей вывода $OUTPUT_RECORD_SEPARATOR ($\), если он не равен nil. См. Построчный ввод-вывод.
Если передан аргумент objects, для каждого объекта выполняются следующие действия:
-
Если объект не является строкой, преобразует его с помощью метода
to_s. -
Записывает его в поток.
-
Если объект не последний, записывает разделитель полей вывода
$OUTPUT_FIELD_SEPARATOR($,), если он не равенnil.
С разделителями по умолчанию:
f = File.open('t.tmp', 'w+')
objects = [0, 0.0, Rational(0, 1), Complex(0, 0), :zero, 'zero']
p $OUTPUT_RECORD_SEPARATOR
p $OUTPUT_FIELD_SEPARATOR
f.print(*objects)
f.rewind
p f.read
f.close
Вывод:
nil nil "00.00/10+0izerozero"
С указанными разделителями:
$\ = "\n" $, = ',' f.rewind f.print(*objects) f.rewind p f.read
Вывод:
"0,0.0,0/1,0+0i,zero,zero\n"
Если аргументы не указаны, записывает содержимое $_ (обычно это последний пользовательский ввод):
f = File.open('t.tmp', 'w+')
gets # Sets $_ to the most recent user input.
f.print
f.close
VALUE
rb_io_printf(int argc, const VALUE *argv, VALUE out)
{
rb_io_write(out, rb_f_sprintf(argc, argv));
return Qnil;
} Форматирует и записывает objects в поток.
Подробнее о format_string см. в разделе Спецификации формата.
static VALUE
rb_io_putc(VALUE io, VALUE ch)
{
VALUE str;
if (RB_TYPE_P(ch, T_STRING)) {
str = rb_str_substr(ch, 0, 1);
}
else {
char c = NUM2CHR(ch);
str = rb_str_new(&c, 1);
}
rb_io_write(io, str);
return ch;
} Записывает символ в поток. См. Символьный ввод-вывод.
Если object — число, при необходимости преобразует его в целое число, а затем записывает символ, код которого равен младшему байту; если object — строка, записывает первый символ:
$stdout.putc "A" $stdout.putc 65
Вывод:
AA
VALUE
rb_io_puts(int argc, const VALUE *argv, VALUE out)
{
VALUE line, args[2];
/* if no argument given, print newline. */
if (argc == 0) {
rb_io_write(out, rb_default_rs);
return Qnil;
}
for (int i = 0; i < argc; i++) {
// Convert the argument to a string:
if (RB_TYPE_P(argv[i], T_STRING)) {
line = argv[i];
}
else if (rb_exec_recursive(io_puts_ary, argv[i], out)) {
continue;
}
else {
line = rb_obj_as_string(argv[i]);
}
// Write the line:
int n = 0;
if (RSTRING_LEN(line) == 0) {
args[n++] = rb_default_rs;
}
else {
args[n++] = line;
if (!rb_str_end_with_asciichar(line, '\n')) {
args[n++] = rb_default_rs;
}
}
rb_io_writev(out, n, args);
}
return Qnil;
} Записывает переданные objects в поток, открытый для записи; возвращает nil.\ После каждой строки, которая ещё не заканчивается последовательностью символов новой строки, записывает символ новой строки. Если вызван без аргументов, записывает символ новой строки. См. Построчный ввод-вывод.
Обратите внимание: каждый добавленный символ новой строки — это символ "\n"<//tt>, not the output record separator (<tt>$\).
Обработка каждого объекта:
-
Строка: записывает строку.
-
Объект, не являющийся ни строкой, ни массивом: записывает
object.to_s. -
Массив: записывает каждый элемент массива; массивы могут быть вложенными.
Чтобы примеры были короче, определим вспомогательный метод:
def show(*objects)
# Puts objects to file.
f = File.new('t.tmp', 'w+')
f.puts(objects)
# Return file content.
f.rewind
p f.read
f.close
end
# Strings without newlines.
show('foo', 'bar', 'baz') # => "foo\nbar\nbaz\n"
# Strings, some with newlines.
show("foo\n", 'bar', "baz\n") # => "foo\nbar\nbaz\n"
# Neither strings nor arrays:
show(0, 0.0, Rational(0, 1), Complex(9, 0), :zero)
# => "0\n0.0\n0/1\n9+0i\nzero\n"
# Array of strings.
show(['foo', "bar\n", 'baz']) # => "foo\nbar\nbaz\n"
# Nested arrays.
show([[[0, 1], 2, 3], 4, 5]) # => "0\n1\n2\n3\n4\n5\n"
static VALUE
rb_io_pwrite(VALUE io, VALUE str, VALUE offset)
{
rb_io_t *fptr;
ssize_t n;
struct prdwr_internal_arg arg;
VALUE tmp;
if (!RB_TYPE_P(str, T_STRING))
str = rb_obj_as_string(str);
arg.offset = NUM2OFFT(offset);
io = GetWriteIO(io);
GetOpenFile(io, fptr);
rb_io_check_writable(fptr);
arg.io = fptr;
arg.fd = fptr->fd;
tmp = rb_str_tmp_frozen_acquire(str);
arg.buf = RSTRING_PTR(tmp);
arg.count = (size_t)RSTRING_LEN(tmp);
n = (ssize_t)pwrite_internal_call((VALUE)&arg);
if (n < 0) rb_sys_fail_path(fptr->pathv);
rb_str_tmp_frozen_release(str, tmp);
return SSIZET2NUM(n);
} Работает как IO#write, за исключением того, что метод:
-
Записывает данные по заданному
offset(в байтах). -
Не учитывает и не изменяет позицию потока (см. Позиция).
-
Обходит буферизацию потока в пользовательском пространстве.
Поскольку этот метод не изменяет состояние потока (в частности, его позицию), pwrite позволяет нескольким потокам и процессам использовать один и тот же объект IO для записи данных со смещениями.
f = File.open('t.tmp', 'w+')
# Write 6 bytes at offset 3.
f.pwrite('ABCDEF', 3) # => 6
f.rewind
f.read # => "\u0000\u0000\u0000ABCDEF"
f.close
Недоступен на некоторых платформах.
static VALUE
console_raw(int argc, VALUE *argv, VALUE io)
{
rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts);
return ttymode(io, rb_yield, io, set_rawmode, optp);
} Передаёт self в блок в режиме raw и возвращает результат выполнения блока.
STDIN.raw(&:gets)
считывает и возвращает строку без эхо-вывода и редактирования строки.
Параметр min задаёт минимальное количество байтов, которое должно быть получено при выполнении операции чтения. (по умолчанию: 1)
Параметр time задаёт время ожидания в секундах с точностью до 1/10 секунды. (по умолчанию: 0)
Если параметр intr равен true, включаются специальные символы break, interrupt, quit и suspend.
Дополнительные сведения см. на странице руководства termios.
Чтобы использовать этот метод, необходимо подключить ‘io/console’.
static VALUE
console_set_raw(int argc, VALUE *argv, VALUE io)
{
conmode t;
rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts);
int fd = GetReadFD(io);
if (!getattr(fd, &t)) sys_fail(io);
set_rawmode(&t, optp);
if (!setattr(fd, &t)) sys_fail(io);
return io;
} Включает режим raw и возвращает io.
Чтобы восстановить режим терминала, используйте io.raw { ... }.
Сведения о параметрах см. в IO#raw.
Чтобы использовать этот метод, необходимо подключить ‘io/console’.
static VALUE
io_read(int argc, VALUE *argv, VALUE io)
{
rb_io_t *fptr;
long n, len;
VALUE length, str;
int shrinkable;
#if RUBY_CRLF_ENVIRONMENT
int previous_mode;
#endif
rb_scan_args(argc, argv, "02", &length, &str);
if (NIL_P(length)) {
GetOpenFile(io, fptr);
rb_io_check_char_readable(fptr);
return read_all(fptr, remain_size(fptr), str);
}
len = NUM2LONG(length);
if (len < 0) {
rb_raise(rb_eArgError, "negative length %ld given", len);
}
shrinkable = io_setstrbuf(&str,len);
GetOpenFile(io, fptr);
rb_io_check_byte_readable(fptr);
if (len == 0) {
io_set_read_length(str, 0, shrinkable);
return str;
}
READ_CHECK(fptr);
#if RUBY_CRLF_ENVIRONMENT
previous_mode = set_binary_mode_with_seek_cur(fptr);
#endif
n = io_fread(str, 0, len, fptr);
io_set_read_length(str, n, shrinkable);
#if RUBY_CRLF_ENVIRONMENT
if (previous_mode == O_TEXT) {
setmode(fptr->fd, O_TEXT);
}
#endif
if (n == 0) return Qnil;
return str;
} Считывает байты из потока; поток должен быть открыт для чтения (см. Режимы доступа):
-
Если
maxlenравноnil, считывает все байты в режиме данных потока. -
В противном случае считывает до
maxlenбайтов в двоичном режиме.
Возвращает строку (новую или переданную out_string), содержащую считанные байты. Кодировка строки зависит от maxLen и out_string:
-
maxlen—nil: используется внутренняя кодировкаself(независимо от того, передан лиout_string). -
maxlenнеnil:-
Передан
out_string: кодировкаout_stringне изменяется. -
out_stringне передан: используется ASCII-8BIT.
-
Без аргумента out_string
Если аргумент out_string не указан, возвращаемым значением является новая строка:
f = File.new('t.txt')
f.read
# => "First line\nSecond line\n\nFourth line\nFifth line\n"
f.rewind
f.read(30) # => "First line\r\nSecond line\r\n\r\nFou"
f.read(30) # => "rth line\r\nFifth line\r\n"
f.read(30) # => nil
f.close
Если maxlen равен нулю, возвращается пустая строка.
С аргументом out_string
Если аргумент out_string указан, возвращается out_string, содержимое которого заменяется:
f = File.new('t.txt')
s = 'foo' # => "foo"
f.read(nil, s) # => "First line\nSecond line\n\nFourth line\nFifth line\n"
s # => "First line\nSecond line\n\nFourth line\nFifth line\n"
f.rewind
s = 'bar'
f.read(30, s) # => "First line\r\nSecond line\r\n\r\nFou"
s # => "First line\r\nSecond line\r\n\r\nFou"
s = 'baz'
f.read(30, s) # => "rth line\r\nFifth line\r\n"
s # => "rth line\r\nFifth line\r\n"
s = 'bat'
f.read(30, s) # => nil
s # => ""
f.close
Обратите внимание, что этот метод работает подобно функции fread() в C. Это означает, что он повторно вызывает системные вызовы read(2), чтобы считать данные заданного размера maxlen (или до EOF).
Такое поведение сохраняется, даже если поток работает в неблокирующем режиме. (Как и другие методы, этот метод не зависит от флага неблокирующего режима.)
Если требуется поведение, аналогичное одному системному вызову read(2), рассмотрите методы readpartial, read_nonblock и sysread.
Связанный метод: IO#write.
# File io.rb, line 62 def read_nonblock(len, buf = nil, exception: true) Primitive.io_read_nonblock(len, buf, exception) end
Считывает не более maxlen байтов из ios с помощью системного вызова read(2) после установки O_NONBLOCK для базового файлового дескриптора.
Если указан необязательный аргумент outbuf, он должен ссылаться на String, в который будут помещены данные. После вызова метода outbuf будет содержать только полученные данные, даже если изначально он был непустым.
read_nonblock просто вызывает системный вызов read(2). Он вызывает все ошибки, которые вызывает системный вызов read(2): Errno::EWOULDBLOCK, Errno::EINTR и т. д. Вызывающая сторона должна обрабатывать такие ошибки.
Если возникает исключение Errno::EWOULDBLOCK или Errno::EAGAIN, к нему добавляется IO::WaitReadable. Поэтому для перехвата этих исключений и повторного вызова read_nonblock можно использовать IO::WaitReadable.
read_nonblock вызывает EOFError при достижении EOF.
На некоторых платформах, например Windows, неблокирующий режим не поддерживается для объектов IO, отличных от сокетов. В таких случаях возникает Errno::EBADF.
Если буфер чтения байтов не пуст, read_nonblock считывает данные из буфера, как readpartial. В этом случае системный вызов read(2) не выполняется.
Если read_nonblock вызывает исключение типа IO::WaitReadable, для предотвращения активного ожидания не следует вызывать read_nonblock, пока io не станет доступен для чтения. Это можно сделать следующим образом.
# emulates blocking read (readpartial). begin result = io.read_nonblock(maxlen) rescue IO::WaitReadable IO.select([io]) retry end
Хотя IO#read_nonblock не вызывает IO::WaitWritable, метод OpenSSL::Buffering#read_nonblock может вызвать IO::WaitWritable. Если IO и SSL должны использоваться полиморфно, следует также перехватывать IO::WaitWritable. Пример кода см. в документации к OpenSSL::Buffering#read_nonblock.
Обратите внимание, что этот метод идентичен readpartial, за исключением установленного флага неблокирующего режима.
Указав ключевой аргумент exception для false, можно задать, чтобы read_nonblock не вызывал исключение IO::WaitReadable, а вместо этого возвращал символ :wait_readable. При достижении EOF метод вернёт nil, а не вызовет EOFError.
static VALUE
rb_io_readbyte(VALUE io)
{
VALUE c = rb_io_getbyte(io);
if (NIL_P(c)) {
rb_eof_error();
}
return c;
} Считывает и возвращает следующий байт (в диапазоне 0..255) из потока; если поток уже достиг конца, вызывает EOFError. См. Ввод-вывод байтов.
f = File.open('t.txt')
f.readbyte # => 70
f.close
f = File.open('t.rus')
f.readbyte # => 209
f.close
Связанный метод: IO#getbyte (не вызывает EOFError).
static VALUE
rb_io_readchar(VALUE io)
{
VALUE c = rb_io_getc(io);
if (NIL_P(c)) {
rb_eof_error();
}
return c;
} Считывает и возвращает следующую строку из одного символа из потока; если поток уже достиг конца, вызывает EOFError. См. Ввод-вывод символов.
f = File.open('t.txt')
f.readchar # => "F"
f.close
f = File.open('t.rus')
f.readchar.ord # => 1090
f.close
# File io.rb, line 133 def readline(sep = $/, limit = nil, chomp: false) Primitive.io_readline(sep, limit, chomp) end
static VALUE
rb_io_readlines(int argc, VALUE *argv, VALUE io)
{
struct getline_arg args;
prepare_getline_args(argc, argv, &args, io);
return io_readlines(&args, io);
} Считывает и возвращает все оставшиеся строки из потока; не изменяет $_. См. Построчный ввод-вывод.
Если аргументы не указаны, возвращает строки, разделённые разделителем строк $/, или nil, если разделитель не задан:
f = File.new('t.txt')
f.readlines
# => ["First line\n", "Second line\n", "\n", "Fourth line\n", "Fifth line\n"]
f.readlines # => []
f.close
Если указан только строковый аргумент sep, возвращает строки, разделённые разделителем строк sep, или nil, если разделитель не задан; см. Разделитель строк:
f = File.new('t.txt')
f.readlines('li')
# => ["First li", "ne\nSecond li", "ne\n\nFourth li", "ne\nFifth li", "ne\n"]
f.close
Учитываются два специальных значения sep:
f = File.new('t.txt')
# Get all into one string.
f.readlines(nil)
# => ["First line\nSecond line\n\nFourth line\nFifth line\n"]
# Get paragraphs (up to two line separators).
f.rewind
f.readlines('')
# => ["First line\nSecond line\n\n", "Fourth line\nFifth line\n"]
f.close
Если указан только целочисленный аргумент limit, ограничивает количество байтов в каждой строке; см. Ограничение длины строки:
f = File.new('t.txt')
f.readlines(8)
# => ["First li", "ne\n", "Second l", "ine\n", "\n", "Fourth l", "ine\n", "Fifth li", "ne\n"]
f.close
Если указаны аргументы sep и limit, объединяет оба варианта поведения (см. Разделитель строк и ограничение длины строки).
Необязательный ключевой аргумент chomp задаёт, следует ли исключать разделители строк:
f = File.new('t.txt')
f.readlines(chomp: true)
# => ["First line", "Second line", "", "Fourth line", "Fifth line"]
f.close
static VALUE
io_readpartial(int argc, VALUE *argv, VALUE io)
{
VALUE ret;
ret = io_getpartial(argc, argv, io, Qnil, 0);
if (NIL_P(ret))
rb_eof_error();
return ret;
} Считывает из потока до maxlen байтов; возвращает строку (новую или переданную out_string). Её кодировка:
-
Неизменённая кодировка
out_string, если указанout_string. -
В противном случае — ASCII-8BIT.
-
Содержит
maxlenбайтов из потока, если они доступны. -
В противном случае содержит все доступные байты, если таковые имеются.
-
В противном случае является пустой строкой.
Если указан единственный неотрицательный целочисленный аргумент maxlen, возвращает новую строку:
f = File.new('t.txt')
f.readpartial(20) # => "First line\nSecond l"
f.readpartial(20) # => "ine\n\nFourth line\n"
f.readpartial(20) # => "Fifth line\n"
f.readpartial(20) # Raises EOFError.
f.close
Если указаны оба аргумента maxlen и строковый аргумент out_string, возвращает изменённый out_string:
f = File.new('t.txt')
s = 'foo'
f.readpartial(20, s) # => "First line\nSecond l"
s = 'bar'
f.readpartial(0, s) # => ""
f.close
Этот метод полезен для работы с такими потоками, как каналы, сокеты или tty. Он блокируется, только если данные недоступны немедленно. Это означает, что он блокируется, только если выполняются все следующие условия:
-
Буфер байтов в потоке пуст.
-
Содержимое потока пусто.
-
Поток не достиг EOF.
При блокировке метод ожидает появления дополнительных данных или EOF в потоке:
-
Если считываются дополнительные данные, метод возвращает их.
-
Если достигнут EOF, метод вызывает
EOFError.
Если блокировка не требуется, метод немедленно возвращает результат:
-
Возвращает данные из буфера, если они есть.
-
В противном случае возвращает данные из потока, если они есть.
-
В противном случае вызывает
EOFError, если поток достиг EOF.
Обратите внимание, что этот метод похож на sysread. Отличия:
-
Если буфер байтов не пуст, данные считываются из буфера байтов, а не с помощью «sysread для буферизованного
IO(IOError)». -
Он не вызывает Errno::EWOULDBLOCK и Errno::EINTR. Если при вызове системного вызова чтения readpartial встречает EWOULDBLOCK или EINTR, readpartial повторяет системный вызов.
Последнее означает, что readpartial не зависит от флага неблокирующего режима. Он блокируется в ситуации, когда IO#sysread вызывает Errno::EWOULDBLOCK, как если бы файловый дескриптор работал в блокирующем режиме.
Примеры:
# # Returned Buffer Content Pipe Content r, w = IO.pipe # w << 'abc' # "" "abc". r.readpartial(4096) # => "abc" "" "" r.readpartial(4096) # (Blocks because buffer and pipe are empty.) # # Returned Buffer Content Pipe Content r, w = IO.pipe # w << 'abc' # "" "abc" w.close # "" "abc" EOF r.readpartial(4096) # => "abc" "" EOF r.readpartial(4096) # raises EOFError # # Returned Buffer Content Pipe Content r, w = IO.pipe # w << "abc\ndef\n" # "" "abc\ndef\n" r.gets # => "abc\n" "def\n" "" w << "ghi\n" # "def\n" "ghi\n" r.readpartial(4096) # => "def\n" "" "ghi\n" r.readpartial(4096) # => "ghi\n" "" ""
static VALUE
rb_io_reopen(int argc, VALUE *argv, VALUE file)
{
VALUE fname, nmode, opt;
int oflags;
rb_io_t *fptr;
if (rb_scan_args(argc, argv, "11:", &fname, &nmode, &opt) == 1) {
VALUE tmp = rb_io_check_io(fname);
if (!NIL_P(tmp)) {
return io_reopen(file, tmp);
}
}
FilePathValue(fname);
rb_io_taint_check(file);
fptr = RFILE(file)->fptr;
if (!fptr) {
fptr = RFILE(file)->fptr = ZALLOC(rb_io_t);
}
if (!NIL_P(nmode) || !NIL_P(opt)) {
enum rb_io_mode fmode;
struct rb_io_encoding convconfig;
rb_io_extract_modeenc(&nmode, 0, opt, &oflags, &fmode, &convconfig);
if (RUBY_IO_EXTERNAL_P(fptr) &&
((fptr->mode & FMODE_READWRITE) & (fmode & FMODE_READWRITE)) !=
(fptr->mode & FMODE_READWRITE)) {
rb_raise(rb_eArgError,
"%s can't change access mode from \"%s\" to \"%s\"",
PREP_STDIO_NAME(fptr), rb_io_fmode_modestr(fptr->mode),
rb_io_fmode_modestr(fmode));
}
fptr->mode = fmode;
fptr->encs = convconfig;
}
else {
oflags = rb_io_fmode_oflags(fptr->mode);
}
fptr->pathv = fname;
if (fptr->fd < 0) {
fptr->fd = rb_sysopen(fptr->pathv, oflags, 0666);
fptr->stdio_file = 0;
return file;
}
if (fptr->mode & FMODE_WRITABLE) {
if (io_fflush(fptr) < 0)
rb_sys_fail_on_write(fptr);
}
fptr->rbuf.off = fptr->rbuf.len = 0;
if (fptr->stdio_file) {
int e = rb_freopen(rb_str_encode_ospath(fptr->pathv),
rb_io_oflags_modestr(oflags),
fptr->stdio_file);
if (e) rb_syserr_fail_path(e, fptr->pathv);
fptr->fd = fileno(fptr->stdio_file);
rb_fd_fix_cloexec(fptr->fd);
#ifdef USE_SETVBUF
if (setvbuf(fptr->stdio_file, NULL, _IOFBF, 0) != 0)
rb_warn("setvbuf() can't be honoured for %"PRIsVALUE, fptr->pathv);
#endif
if (fptr->stdio_file == stderr) {
if (setvbuf(fptr->stdio_file, NULL, _IONBF, BUFSIZ) != 0)
rb_warn("setvbuf() can't be honoured for %"PRIsVALUE, fptr->pathv);
}
else if (fptr->stdio_file == stdout && isatty(fptr->fd)) {
if (setvbuf(fptr->stdio_file, NULL, _IOLBF, BUFSIZ) != 0)
rb_warn("setvbuf() can't be honoured for %"PRIsVALUE, fptr->pathv);
}
}
else {
int tmpfd = rb_sysopen(fptr->pathv, oflags, 0666);
int err = 0;
if (rb_cloexec_dup2(tmpfd, fptr->fd) < 0)
err = errno;
(void)close(tmpfd);
if (err) {
rb_syserr_fail_path(err, fptr->pathv);
}
}
return file;
} Связывает поток с другим потоком, который может принадлежать к другому классу. Этот метод можно использовать для перенаправления существующего потока в новое место назначения.
Если указан аргумент other_io, связывает поток с этим потоком:
# Redirect $stdin from a file.
f = File.open('t.txt')
$stdin.reopen(f)
f.close
# Redirect $stdout to a file.
f = File.open('t.tmp', 'w')
$stdout.reopen(f)
f.close
Если указан аргумент path, связывает поток с новым потоком, открытым по этому пути к файлу:
$stdin.reopen('t.txt')
$stdout.reopen('t.tmp', 'w')
Необязательные ключевые аргументы opts задают:
static VALUE
rb_io_rewind(VALUE io)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
if (io_seek(fptr, 0L, 0) < 0 && errno) rb_sys_fail_path(fptr->pathv);
if (io == ARGF.current_file) {
ARGF.lineno -= fptr->lineno;
}
fptr->lineno = 0;
if (fptr->readconv) {
clear_readconv(fptr);
}
return INT2FIX(0);
} Перемещает поток в начало, устанавливая позицию и номер строки в ноль; см. Позиция и Номер строки:
f = File.open('t.txt')
f.tell # => 0
f.lineno # => 0
f.gets # => "First line\n"
f.tell # => 12
f.lineno # => 1
f.rewind # => 0
f.tell # => 0
f.lineno # => 0
f.close
Обратите внимание, что этот метод нельзя использовать с такими потоками, как каналы, tty и сокеты.
static VALUE
console_scroll_backward(VALUE io, VALUE val)
{
return console_scroll(io, -NUM2INT(val));
} Прокручивает всю область прокрутки назад на n строк.
Чтобы использовать этот метод, необходимо подключить ‘io/console’.
static VALUE
console_scroll_forward(VALUE io, VALUE val)
{
return console_scroll(io, +NUM2INT(val));
} Прокручивает всю область прокрутки вперёд на n строк.
Чтобы использовать этот метод, необходимо подключить ‘io/console’.
static VALUE
rb_io_seek_m(int argc, VALUE *argv, VALUE io)
{
VALUE offset, ptrname;
int whence = SEEK_SET;
if (rb_scan_args(argc, argv, "11", &offset, &ptrname) == 2) {
whence = interpret_seek_whence(ptrname);
}
return rb_io_seek(io, offset, whence);
} Перемещается к позиции, заданной целым числом offset (см. Позиция) и константой whence, которая может принимать одно из следующих значений:
-
:CURилиIO::SEEK_CUR: перемещает поток в текущую позицию плюс заданное значениеoffset:f = File.open('t.txt') f.tell # => 0 f.seek(20, :CUR) # => 0 f.tell # => 20 f.seek(-10, :CUR) # => 0 f.tell # => 10 f.close -
:ENDилиIO::SEEK_END: перемещает поток в конец плюс заданное значениеoffset:f = File.open('t.txt') f.tell # => 0 f.seek(0, :END) # => 0 # Repositions to stream end. f.tell # => 52 f.seek(-20, :END) # => 0 f.tell # => 32 f.seek(-40, :END) # => 0 f.tell # => 12 f.close -
:SETилиIO:SEEK_SET: перемещает поток в заданную позициюoffset:f = File.open('t.txt') f.tell # => 0 f.seek(20, :SET) # => 0 f.tell # => 20 f.seek(40, :SET) # => 0 f.tell # => 40 f.close
static VALUE
rb_io_set_encoding(int argc, VALUE *argv, VALUE io)
{
rb_io_t *fptr;
VALUE v1, v2, opt;
if (!RB_TYPE_P(io, T_FILE)) {
return forward(io, id_set_encoding, argc, argv);
}
argc = rb_scan_args(argc, argv, "11:", &v1, &v2, &opt);
GetOpenFile(io, fptr);
io_encoding_set(fptr, v1, v2, opt);
return io;
} См. Кодировки.
Аргумент ext_enc, если указан, должен быть объектом Encoding или String с названием кодировки; он назначается в качестве кодировки потока.
Аргумент int_enc, если указан, должен быть объектом Encoding или String с названием кодировки; он назначается в качестве кодировки внутренней строки.
Аргумент 'ext_enc:int_enc', если указан, представляет собой строку с двумя названиями кодировок, разделёнными двоеточием; соответствующие объекты Encoding назначаются как внешняя и внутренняя кодировки потока.
Если внешняя кодировка строки — двоичная/ASCII-8BIT, внутренняя кодировка строки устанавливается в nil, поскольку транскодирование не требуется.
Необязательные ключевые аргументы enc_opts задают параметры кодировки.
static VALUE
rb_io_set_encoding_by_bom(VALUE io)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
if (!(fptr->mode & FMODE_BINMODE)) {
rb_raise(rb_eArgError, "ASCII incompatible encoding needs binmode");
}
if (fptr->encs.enc2) {
rb_raise(rb_eArgError, "encoding conversion is set");
}
else if (fptr->encs.enc && fptr->encs.enc != rb_ascii8bit_encoding()) {
rb_raise(rb_eArgError, "encoding is set to %s already",
rb_enc_name(fptr->encs.enc));
}
if (!io_set_encoding_by_bom(io)) return Qnil;
return rb_enc_from_encoding(fptr->encs.enc);
} Если поток начинается с BOM (метки порядка байтов), удаляет BOM и задаёт соответствующую внешнюю кодировку; возвращает найденную кодировку или nil, если кодировка не найдена:
File.write('t.tmp', "\u{FEFF}abc")
io = File.open('t.tmp', 'rb')
io.set_encoding_by_bom # => #<Encoding:UTF-8>
io.close
File.write('t.tmp', 'abc')
io = File.open('t.tmp', 'rb')
io.set_encoding_by_bom # => nil
io.close
Вызывает исключение, если поток работает не в двоичном режиме или его кодировка уже задана.
static VALUE
rb_io_stat(VALUE obj)
{
rb_io_t *fptr;
rb_io_stat_data st;
GetOpenFile(obj, fptr);
if (fstatx_without_gvl(fptr, &st, STATX_ALL) == -1) {
rb_sys_fail_path(fptr->pathv);
}
return rb_statx_new(&st);
} Возвращает сведения о состоянии ios в виде объекта типа File::Stat.
f = File.new("testfile")
s = f.stat
"%o" % s.mode #=> "100644"
s.blksize #=> 4096
s.atime #=> Wed Apr 09 08:53:54 CDT 2003
static VALUE
rb_io_sync(VALUE io)
{
rb_io_t *fptr;
io = GetWriteIO(io);
GetOpenFile(io, fptr);
return RBOOL(fptr->mode & FMODE_SYNC);
} Возвращает текущий режим синхронизации потока. Когда режим синхронизации включён, все выходные данные немедленно передаются базовой операционной системе и не буферизуются Ruby. См. также fsync.
f = File.open('t.tmp', 'w')
f.sync # => false
f.sync = true
f.sync # => true
f.close
static VALUE
rb_io_set_sync(VALUE io, VALUE sync)
{
rb_io_t *fptr;
io = GetWriteIO(io);
GetOpenFile(io, fptr);
if (RTEST(sync)) {
fptr->mode |= FMODE_SYNC;
}
else {
fptr->mode &= ~FMODE_SYNC;
}
return sync;
} Устанавливает для потока заданное значение режима синхронизации; возвращает это значение.
Значения режима синхронизации:
-
true: все выходные данные немедленно передаются базовой операционной системе и не буферизуются внутри. -
false: выходные данные могут буферизоваться внутри.
Пример:
f = File.open('t.tmp', 'w')
f.sync # => false
f.sync = true
f.sync # => true
f.close
Связанный метод: IO#fsync.
static VALUE
rb_io_sysread(int argc, VALUE *argv, VALUE io)
{
VALUE len, str;
rb_io_t *fptr;
long n, ilen;
struct io_internal_read_struct iis;
int shrinkable;
rb_scan_args(argc, argv, "11", &len, &str);
ilen = NUM2LONG(len);
shrinkable = io_setstrbuf(&str, ilen);
if (ilen == 0) return str;
GetOpenFile(io, fptr);
rb_io_check_byte_readable(fptr);
if (READ_DATA_BUFFERED(fptr)) {
rb_raise(rb_eIOError, "sysread for buffered IO");
}
rb_io_check_closed(fptr);
io_setstrbuf(&str, ilen);
iis.th = rb_thread_current();
iis.fptr = fptr;
iis.nonblock = 0;
iis.fd = fptr->fd;
iis.buf = RSTRING_PTR(str);
iis.capa = ilen;
iis.timeout = NULL;
n = io_read_memory_locktmp(str, &iis);
if (n < 0) {
rb_sys_fail_path(fptr->pathv);
}
io_set_read_length(str, n, shrinkable);
if (n == 0 && ilen > 0) {
rb_eof_error();
}
return str;
} Работает как IO#readpartial, но использует низкоуровневые системные функции.
Этот метод не следует использовать вместе с другими методами чтения из потока.
static VALUE
rb_io_sysseek(int argc, VALUE *argv, VALUE io)
{
VALUE offset, ptrname;
int whence = SEEK_SET;
rb_io_t *fptr;
rb_off_t pos;
if (rb_scan_args(argc, argv, "11", &offset, &ptrname) == 2) {
whence = interpret_seek_whence(ptrname);
}
pos = NUM2OFFT(offset);
GetOpenFile(io, fptr);
if ((fptr->mode & FMODE_READABLE) &&
(READ_DATA_BUFFERED(fptr) || READ_CHAR_PENDING(fptr))) {
rb_raise(rb_eIOError, "sysseek for buffered IO");
}
if ((fptr->mode & FMODE_WRITABLE) && fptr->wbuf.len) {
rb_warn("sysseek for buffered IO");
}
errno = 0;
pos = lseek(fptr->fd, pos, whence);
if (pos < 0 && errno) rb_sys_fail_path(fptr->pathv);
return OFFT2NUM(pos);
} Работает как IO#seek, но:
-
Использует низкоуровневые системные функции.
-
Возвращает новую позицию.
static VALUE
rb_io_syswrite(VALUE io, VALUE str)
{
VALUE tmp;
rb_io_t *fptr;
long n, len;
const char *ptr;
if (!RB_TYPE_P(str, T_STRING))
str = rb_obj_as_string(str);
io = GetWriteIO(io);
GetOpenFile(io, fptr);
rb_io_check_writable(fptr);
if (fptr->wbuf.len) {
rb_warn("syswrite for buffered IO");
}
tmp = rb_str_tmp_frozen_acquire(str);
RSTRING_GETMEM(tmp, ptr, len);
n = rb_io_write_memory(fptr, ptr, len);
if (n < 0) rb_sys_fail_path(fptr->pathv);
rb_str_tmp_frozen_release(str, tmp);
return LONG2FIX(n);
} Записывает заданный object в текущий поток, который должен быть открыт для записи (см. «Режимы»); возвращает количество записанных байтов. Если object не является строкой, он преобразуется с помощью метода to_s:
f = File.new('t.tmp', 'w')
f.syswrite('foo') # => 3
f.syswrite(30) # => 2
f.syswrite(:foo) # => 3
f.close
Этот метод не следует использовать вместе с другими методами записи в поток.
static VALUE
rb_io_tell(VALUE io)
{
rb_io_t *fptr;
rb_off_t pos;
GetOpenFile(io, fptr);
pos = io_tell(fptr);
if (pos < 0 && errno) rb_sys_fail_path(fptr->pathv);
pos -= fptr->rbuf.len;
return OFFT2NUM(pos);
} Возвращает текущую позицию (в байтах) в self (см. Позиция):
f = File.open('t.txt')
f.tell # => 0
f.gets # => "First line\n"
f.tell # => 12
f.close
VALUE
rb_io_timeout(VALUE self)
{
rb_io_t *fptr = rb_io_get_fptr(self);
return fptr->timeout;
} Возвращает внутреннюю длительность времени ожидания или nil, если она не задана.
VALUE
rb_io_set_timeout(VALUE self, VALUE timeout)
{
// Validate it:
if (RTEST(timeout)) {
rb_time_interval(timeout);
}
rb_io_t *fptr = rb_io_get_fptr(self);
fptr->timeout = timeout;
return self;
} Устанавливает внутреннее время ожидания в указанную длительность или nil. По возможности это время применяется ко всем блокирующим операциям.
Если операция длится дольше заданного времени ожидания, вызывается IO::TimeoutError.
Это влияет, помимо прочего, на следующие методы: gets, puts, read, write, wait_readable и wait_writable. Это также влияет на блокирующие операции с сокетами, такие как Socket#accept и Socket#connect.
На некоторые операции, например File#open и IO#close, время ожидания не влияет. Истечение времени ожидания во время операции записи может оставить IO в несогласованном состоянии, например, если данные были записаны частично. В целом, время ожидания — это крайняя мера, позволяющая предотвратить зависание приложения при медленных операциях ввода-вывода, например во время атаки Slowloris.
static VALUE
rb_io_to_io(VALUE io)
{
return io;
} Возвращает self.
static VALUE
console_ttyname(VALUE io)
{
int fd = rb_io_descriptor(io);
if (!isatty(fd)) return Qnil;
# if defined _WIN32
return rb_usascii_str_new_lit("con");
# elif defined HAVE_TTYNAME_R
{
char termname[1024], *tn = termname;
size_t size = sizeof(termname);
int e;
if (ttyname_r(fd, tn, size) == 0)
return rb_interned_str_cstr(tn);
if ((e = errno) == ERANGE) {
VALUE s = rb_str_new(0, size);
while (1) {
tn = RSTRING_PTR(s);
size = rb_str_capacity(s);
if (ttyname_r(fd, tn, size) == 0) {
return rb_str_to_interned_str(rb_str_resize(s, strlen(tn)));
}
if ((e = errno) != ERANGE) break;
if ((size *= 2) >= INT_MAX/2) break;
rb_str_resize(s, size);
}
}
rb_syserr_fail_str(e, rb_sprintf("ttyname_r(%d)", fd));
UNREACHABLE_RETURN(Qnil);
}
# elif defined HAVE_TTYNAME
{
const char *tn = ttyname(fd);
if (!tn) {
int e = errno;
rb_syserr_fail_str(e, rb_sprintf("ttyname(%d)", fd));
}
return rb_interned_str_cstr(tn);
}
# else
# error No ttyname function
# endif
} Возвращает имя связанного терминала (tty), если io не является tty. В противном случае возвращает nil.
VALUE
rb_io_ungetbyte(VALUE io, VALUE b)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
rb_io_check_byte_readable(fptr);
switch (TYPE(b)) {
case T_NIL:
return Qnil;
case T_FIXNUM:
case T_BIGNUM: ;
VALUE v = rb_int_modulo(b, INT2FIX(256));
unsigned char c = NUM2INT(v) & 0xFF;
b = rb_str_new((const char *)&c, 1);
break;
default:
StringValue(b);
}
io_ungetbyte(b, fptr);
return Qnil;
} Помещает указанные данные обратно («сдвигает в начало») в буфер потока, располагая их так, чтобы они были прочитаны следующими; возвращает nil. См. Байтовый ввод-вывод.
Обратите внимание:
-
Вызов этого метода не влияет на чтение без буферизации (например,
IO#sysread). -
Вызов
rewindдля потока удаляет помещённые обратно данные.
Если указан аргумент integer, используется только его младший байт:
File.write('t.tmp', '012')
f = File.open('t.tmp')
f.ungetbyte(0x41) # => nil
f.read # => "A012"
f.rewind
f.ungetbyte(0x4243) # => nil
f.read # => "C012"
f.close
Если указан аргумент string, используются все байты:
File.write('t.tmp', '012')
f = File.open('t.tmp')
f.ungetbyte('A') # => nil
f.read # => "A012"
f.rewind
f.ungetbyte('BCDE') # => nil
f.read # => "BCDE012"
f.close
VALUE
rb_io_ungetc(VALUE io, VALUE c)
{
rb_io_t *fptr;
long len;
GetOpenFile(io, fptr);
rb_io_check_char_readable(fptr);
if (FIXNUM_P(c)) {
c = rb_enc_uint_chr(FIX2UINT(c), io_read_encoding(fptr));
}
else if (RB_BIGNUM_TYPE_P(c)) {
c = rb_enc_uint_chr(NUM2UINT(c), io_read_encoding(fptr));
}
else {
StringValue(c);
}
if (NEED_READCONV(fptr)) {
SET_BINARY_MODE(fptr);
len = RSTRING_LEN(c);
#if SIZEOF_LONG > SIZEOF_INT
if (len > INT_MAX)
rb_raise(rb_eIOError, "ungetc failed");
#endif
make_readconv(fptr, (int)len);
if (fptr->cbuf.capa - fptr->cbuf.len < len)
rb_raise(rb_eIOError, "ungetc failed");
if (fptr->cbuf.off < len) {
MEMMOVE(fptr->cbuf.ptr+fptr->cbuf.capa-fptr->cbuf.len,
fptr->cbuf.ptr+fptr->cbuf.off,
char, fptr->cbuf.len);
fptr->cbuf.off = fptr->cbuf.capa-fptr->cbuf.len;
}
fptr->cbuf.off -= (int)len;
fptr->cbuf.len += (int)len;
MEMMOVE(fptr->cbuf.ptr+fptr->cbuf.off, RSTRING_PTR(c), char, len);
}
else {
NEED_NEWLINE_DECORATOR_ON_READ_CHECK(fptr);
io_ungetbyte(c, fptr);
}
return Qnil;
} Помещает указанные данные обратно («сдвигает в начало») в буфер потока, располагая их так, чтобы они были прочитаны следующими; возвращает nil. См. Символьный ввод-вывод.
Обратите внимание:
-
Вызов этого метода не влияет на чтение без буферизации (например,
IO#sysread). -
Вызов
rewindдля потока удаляет помещённые обратно данные.
Если указан аргумент integer, целое число интерпретируется как символ:
File.write('t.tmp', '012')
f = File.open('t.tmp')
f.ungetc(0x41) # => nil
f.read # => "A012"
f.rewind
f.ungetc(0x0442) # => nil
f.getc.ord # => 1090
f.close
Если указан аргумент string, используются все символы:
File.write('t.tmp', '012')
f = File.open('t.tmp')
f.ungetc('A') # => nil
f.read # => "A012"
f.rewind
f.ungetc("\u0442\u0435\u0441\u0442") # => nil
f.getc.ord # => 1090
f.getc.ord # => 1077
f.getc.ord # => 1089
f.getc.ord # => 1090
f.close
static VALUE
io_wait(int argc, VALUE *argv, VALUE io)
{
VALUE timeout = Qundef;
enum rb_io_event events = 0;
int return_io = 0;
if (argc != 2 || (RB_SYMBOL_P(argv[0]) || RB_SYMBOL_P(argv[1]))) {
// We'd prefer to return the actual mask, but this form would return the io itself:
return_io = 1;
// Slow/messy path:
for (int i = 0; i < argc; i += 1) {
if (RB_SYMBOL_P(argv[i])) {
events |= wait_mode_sym(argv[i]);
}
else if (UNDEF_P(timeout)) {
rb_time_interval(timeout = argv[i]);
}
else {
rb_raise(rb_eArgError, "timeout given more than once");
}
}
if (UNDEF_P(timeout)) timeout = Qnil;
if (events == 0) {
events = RUBY_IO_READABLE;
}
}
else /* argc == 2 and neither are symbols */ {
// This is the fast path:
events = io_event_from_value(argv[0]);
timeout = argv[1];
}
if (events & RUBY_IO_READABLE) {
rb_io_t *fptr = NULL;
RB_IO_POINTER(io, fptr);
if (rb_io_read_pending(fptr)) {
// This was the original behaviour:
if (return_io) return Qtrue;
// New behaviour always returns an event mask:
else return RB_INT2NUM(RUBY_IO_READABLE);
}
}
return io_wait_event(io, events, timeout, return_io);
} Ожидает, пока IO не будет готов к указанным событиям, и возвращает подмножество событий, которые стали готовы, либо ложное значение по истечении времени ожидания.
События могут представлять собой битовую маску из IO::READABLE, IO::WRITABLE или IO::PRIORITY.
Если буферизованные данные доступны, немедленно возвращает маску событий (истинное значение).
Во второй форме, если передан один или несколько символов событий (:read, :write или :read_write), маска событий представляет собой побитовое ИЛИ битовых масок, соответствующих этим символам. В этой форме timeout является необязательным, порядок аргументов произвольный; метод возвращает io, если готово любое из событий.
static VALUE
io_wait_priority(int argc, VALUE *argv, VALUE io)
{
rb_io_t *fptr = NULL;
RB_IO_POINTER(io, fptr);
rb_io_check_char_readable(fptr);
if (rb_io_read_pending(fptr)) return Qtrue;
rb_check_arity(argc, 0, 1);
VALUE timeout = argc == 1 ? argv[0] : Qnil;
return io_wait_event(io, RUBY_IO_PRIORITY, timeout, 1);
} Ожидает, пока IO не станет готовым к обработке данных с приоритетом, и возвращает истинное значение либо ложное значение по истечении времени ожидания. Данные с приоритетом отправляются и принимаются с помощью флага Socket::MSG_OOB и обычно используются только для потоков.
static VALUE
io_wait_readable(int argc, VALUE *argv, VALUE io)
{
rb_io_t *fptr;
RB_IO_POINTER(io, fptr);
rb_io_check_char_readable(fptr);
if (rb_io_read_pending(fptr)) return Qtrue;
rb_check_arity(argc, 0, 1);
VALUE timeout = (argc == 1 ? argv[0] : Qnil);
return io_wait_event(io, RUBY_IO_READABLE, timeout, 1);
} Ожидает, пока IO не станет доступен для чтения, и возвращает истинное значение либо ложное значение по истечении времени ожидания. Если буферизованные данные доступны, немедленно возвращает истинное значение.
static VALUE
io_wait_writable(int argc, VALUE *argv, VALUE io)
{
rb_io_t *fptr;
RB_IO_POINTER(io, fptr);
rb_io_check_writable(fptr);
rb_check_arity(argc, 0, 1);
VALUE timeout = (argc == 1 ? argv[0] : Qnil);
return io_wait_event(io, RUBY_IO_WRITABLE, timeout, 1);
} Ожидает, пока IO не станет доступен для записи, и возвращает истинное значение либо ложное значение по истечении времени ожидания.
static VALUE
console_winsize(VALUE io)
{
rb_console_size_t ws;
int fd = GetWriteFD(io);
if (!getwinsize(fd, &ws)) sys_fail(io);
return rb_assoc_new(INT2NUM(winsize_row(&ws)), INT2NUM(winsize_col(&ws)));
} Возвращает размер консоли.
Чтобы использовать этот метод, необходимо подключить ‘io/console’.
static VALUE
console_set_winsize(VALUE io, VALUE size)
{
rb_console_size_t ws;
#if defined _WIN32
HANDLE wh;
int newrow, newcol;
COORD oldsize;
SMALL_RECT oldwindow;
BOOL ret;
#endif
VALUE row, col, xpixel, ypixel;
const VALUE *sz;
long sizelen;
int fd;
size = rb_Array(size);
if ((sizelen = RARRAY_LEN(size)) != 2 && sizelen != 4) {
rb_raise(rb_eArgError, "wrong number of arguments (given %ld, expected 2 or 4)", sizelen);
}
sz = RARRAY_CONST_PTR(size);
row = sz[0], col = sz[1], xpixel = ypixel = Qnil;
if (sizelen == 4) xpixel = sz[2], ypixel = sz[3];
fd = GetWriteFD(io);
#if defined TIOCSWINSZ
ws.ws_row = ws.ws_col = ws.ws_xpixel = ws.ws_ypixel = 0;
#define SET(m) ws.ws_##m = NIL_P(m) ? 0 : (unsigned short)NUM2UINT(m)
SET(row);
SET(col);
SET(xpixel);
SET(ypixel);
#undef SET
if (!setwinsize(fd, &ws)) sys_fail(io);
#elif defined _WIN32
wh = (HANDLE)rb_w32_get_osfhandle(fd);
#define SET(m) new##m = NIL_P(m) ? 0 : (unsigned short)NUM2UINT(m)
SET(row);
SET(col);
#undef SET
if (!NIL_P(xpixel)) (void)NUM2UINT(xpixel);
if (!NIL_P(ypixel)) (void)NUM2UINT(ypixel);
if (!GetConsoleScreenBufferInfo(wh, &ws)) {
rb_syserr_fail(LAST_ERROR, "GetConsoleScreenBufferInfo");
}
oldsize = ws.dwSize;
oldwindow = ws.srWindow;
if (ws.srWindow.Right + 1 < newcol) {
ws.dwSize.X = newcol;
}
if (ws.dwSize.Y < newrow) {
ws.dwSize.Y = newrow;
}
/* expand screen buffer first if needed */
if (!SetConsoleScreenBufferSize(wh, ws.dwSize)) {
rb_syserr_fail(LAST_ERROR, "SetConsoleScreenBufferInfo");
}
/* refresh ws for new dwMaximumWindowSize */
if (!GetConsoleScreenBufferInfo(wh, &ws)) {
rb_syserr_fail(LAST_ERROR, "GetConsoleScreenBufferInfo");
}
/* check new size before modifying buffer content */
if (newrow <= 0 || newcol <= 0 ||
newrow > ws.dwMaximumWindowSize.Y ||
newcol > ws.dwMaximumWindowSize.X) {
SetConsoleScreenBufferSize(wh, oldsize);
/* remove scrollbar if possible */
SetConsoleWindowInfo(wh, TRUE, &oldwindow);
rb_raise(rb_eArgError, "out of range winsize: [%d, %d]", newrow, newcol);
}
/* shrink screen buffer width */
ws.dwSize.X = newcol;
/* shrink screen buffer height if window height were the same */
if (oldsize.Y == ws.srWindow.Bottom - ws.srWindow.Top + 1) {
ws.dwSize.Y = newrow;
}
ws.srWindow.Left = 0;
ws.srWindow.Right = newcol - 1;
ws.srWindow.Bottom = ws.srWindow.Top + newrow -1;
if (ws.dwCursorPosition.Y > ws.srWindow.Bottom) {
console_scroll(io, ws.dwCursorPosition.Y - ws.srWindow.Bottom);
ws.dwCursorPosition.Y = ws.srWindow.Bottom;
console_goto(io, INT2FIX(ws.dwCursorPosition.Y), INT2FIX(ws.dwCursorPosition.X));
}
if (ws.srWindow.Bottom > ws.dwSize.Y - 1) {
console_scroll(io, ws.srWindow.Bottom - (ws.dwSize.Y - 1));
ws.dwCursorPosition.Y -= ws.srWindow.Bottom - (ws.dwSize.Y - 1);
console_goto(io, INT2FIX(ws.dwCursorPosition.Y), INT2FIX(ws.dwCursorPosition.X));
ws.srWindow.Bottom = ws.dwSize.Y - 1;
}
ws.srWindow.Top = ws.srWindow.Bottom - (newrow - 1);
/* perform changes to winsize */
if (!SetConsoleWindowInfo(wh, TRUE, &ws.srWindow)) {
int last_error = LAST_ERROR;
SetConsoleScreenBufferSize(wh, oldsize);
rb_syserr_fail(last_error, "SetConsoleWindowInfo");
}
/* perform screen buffer shrinking if necessary */
if ((ws.dwSize.Y < oldsize.Y || ws.dwSize.X < oldsize.X) &&
!SetConsoleScreenBufferSize(wh, ws.dwSize)) {
rb_syserr_fail(LAST_ERROR, "SetConsoleScreenBufferInfo");
}
/* remove scrollbar if possible */
if (!SetConsoleWindowInfo(wh, TRUE, &ws.srWindow)) {
rb_syserr_fail(LAST_ERROR, "SetConsoleWindowInfo");
}
#endif
return io;
} Пытается задать размер консоли. Результат зависит от платформы и среды выполнения.
Чтобы использовать этот метод, необходимо подключить ‘io/console’.
static VALUE
io_write_m(int argc, VALUE *argv, VALUE io)
{
if (argc != 1) {
return io_writev(argc, argv, io);
}
else {
VALUE str = argv[0];
return io_write(io, str, 0);
}
} Записывает каждый из указанных объектов objects в self, который должен быть открыт для записи (см. Режимы доступа); возвращает общее количество записанных байтов. Каждый объект objects, не являющийся строкой, преобразуется с помощью метода to_s:
$stdout.write('Hello', ', ', 'World!', "\n") # => 14
$stdout.write('foo', :bar, 2, "\n") # => 8
Вывод:
Hello, World! foobar2
Связанный метод: IO#read.
# File io.rb, line 120 def write_nonblock(buf, exception: true) Primitive.io_write_nonblock(buf, exception) end
Записывает указанную строку в ios с помощью системного вызова write(2) после установки O_NONBLOCK для базового файлового дескриптора.
Возвращает количество записанных байтов.
write_nonblock просто вызывает системный вызов write(2). Он может вызвать любые ошибки, возникающие при системном вызове write(2): Errno::EWOULDBLOCK, Errno::EINTR и т. д. Результат также может быть короче string.length (частичная запись). Вызывающий код должен обрабатывать такие ошибки и частичную запись.
Если возникает исключение Errno::EWOULDBLOCK или Errno::EAGAIN, оно дополняется модулем IO::WaitWritable. Поэтому для перехвата исключений и повторной попытки вызова write_nonblock можно использовать IO::WaitWritable.
# Creates a pipe.
r, w = IO.pipe
# write_nonblock writes only 65536 bytes and return 65536.
# (The pipe size is 65536 bytes on this environment.)
s = "a" * 100000
p w.write_nonblock(s) #=> 65536
# write_nonblock cannot write a byte and raise EWOULDBLOCK (EAGAIN).
p w.write_nonblock("b") # Resource temporarily unavailable (Errno::EAGAIN)
Если буфер записи не пуст, сначала выполняется его сброс.
Если write_nonblock вызывает исключение типа IO::WaitWritable, не следует вызывать write_nonblock, пока io не станет доступен для записи, чтобы избежать активного ожидания. Это можно сделать следующим образом.
begin result = io.write_nonblock(string) rescue IO::WaitWritable, Errno::EINTR IO.select(nil, [io]) retry end
Обратите внимание: это не гарантирует запись всех данных из строки. Длина записанных данных возвращается в результате, и её следует проверить позднее.
На некоторых платформах, например Windows, поддержка write_nonblock зависит от типа объекта IO. В таких случаях write_nonblock вызывает Errno::EBADF.
Указав именованный аргумент exception для false, можно задать, чтобы write_nonblock не вызывал исключение IO::WaitWritable, а вместо этого возвращал символ :wait_writable.
Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.