класс 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 потока с помощью этих методов, которые обычно работают со строками с несколькими байтами:
-
IO#read: Считывает и возвращает некоторые или все оставшиеся байты из потока. -
IO#write: Записывает нуль или более строк в поток; каждый предоставленный объект, который не является строкой, преобразуется с помощьюto_s.
Позиция
Поток IO имеет целое неотрицательное число позиция, которое является смещением байта, в котором будет выполняться следующее чтение или запись. Новый поток имеет позицию ноль (и номер строки ноль); метод rewind сбрасывает позицию (и номер строки) до нуля.
Соответствующие методы:
-
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#eof?(также алиас#eof): Возвращает, находится ли поток в конце потока.
Вы можете переместиться в конец потока, используя метод 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#each_line: Читает каждую оставшуюся строку, передавая её в заданный блок. -
IO#gets: Возвращает следующую строку. -
IO#readline: Какgets, но при достижении конца потока вызывает исключение. -
IO#readlines: Возвращает все оставшиеся строки в массиве.
Каждый из этих методов чтения принимает:
-
Необязательный разделитель строки,
sep; см. Разделитель строки. -
Необязательный лимит размера строки,
limit; см. Лимит строки.
Для каждого из этих методов чтения чтение может начинаться посреди строки, в зависимости от позиции потока; см. Позиция:
f = File.new('t.txt')
f.pos = 27
f.each_line {|line| p line }
f.close
Вывод:
"rth line\n" "Fifth line\n"
Вы можете записывать в поток IO построчно с помощью этого метода:
-
IO#puts: Записывает объекты в поток.
Разделитель строк
Каждый из этих методов использует разделитель строк, который представляет собой строку, разделяющую строки:
По умолчанию разделителем строк является глобальная переменная $/, значение которой по умолчанию "\n". Следующая строка для чтения — все данные от текущей позиции до следующего разделителя строк:
f = File.new('t.txt')
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
Вы можете указать другой разделитель строк:
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
Существует два специальных разделителя строк:
-
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
Лимит строки
Каждый из этих методов использует лимит строки, который указывает, что количество возвращенных байтов не должно быть (намного) длиннее заданного limit;
Многобайтовый символ не будет разделен, поэтому строка может быть немного длиннее заданного предела.
Если limit не задано, строка определяется только 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. -
Но возвращает не более байтов, чем разрешено лимитом.
Пример:
File.open('t.txt') {|f| f.gets('li', 20) } # => "First li"
File.open('t.txt') {|f| f.gets('li', 2) } # => "Fi"
Номер строки
Поток ввода-вывода для чтения имеет целочисленное значение номер строки, неотрицательное.
Соответствующие методы:
-
IO#lineno: Возвращает номер строки. -
IO#lineno=: Сбрасывает и возвращает номер строки.
Если не изменено вызовом метода IO#lineno=, номер строки — это количество строк, прочитанных определёнными методами, ориентированными на строки, в соответствии с заданным разделителем строк sep:
-
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#getc: Читает и возвращает следующий символ из потока. -
IO#readchar: Какgetc, но вызывает исключение в конце потока. -
IO#ungetc: Возвращает (“сдвигает назад”) символ или целое число в поток. -
IO#putc: Записывает символ в поток. -
IO#each_char: Читает каждый оставшийся символ в потоке, передавая символ в заданный блок.
Поток байтов
Вы можете обрабатывать поток ввода-вывода байт за байтом, используя эти методы:
-
IO#getbyte: Возвращает следующий 8-битный байт как целое число в диапазоне 0..255. -
IO#readbyte: Какgetbyte, но вызывает исключение в конце потока. -
IO#ungetbyte: Возвращает (“сдвигает назад”) байт в поток. -
IO#each_byte: Читает каждый оставшийся байт в потоке, передавая байт в заданный блок.
Поток кодовых точек
Вы можете обрабатывать поток ввода-вывода кодовая точка за кодовой точкой:
-
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с новым или существующим потоком ввода-вывода. -
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_read: Закрываетselfдля чтения. -
close_write: Закрываетselfдля записи. -
set_encoding: Устанавливает кодировку дляself. -
set_encoding_by_bom: Устанавливает кодировку дляself, основываясь на её Unicode метке порядка байтов. -
sync=: Устанавливает режим синхронизации на заданное значение.
Запросы
-
autoclose?: Возвращает, будет лиselfавтоматически закрываться. -
binmode?: Возвращает, находится лиselfв бинарном режиме. -
close_on_exec?: Возвращает флаг закрытия при выполнении дляself. -
closed?: Возвращает, закрыт ли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
-
SetПозиция ввода-вывода от текущей позиции - SEEK_DATA
-
SetПозиция ввода-вывода до следующего места, содержащего данные - SEEK_END
-
SetПозиция ввода-вывода от конца - SEEK_HOLE
-
SetПозиция ввода-вывода до следующей дыры - SEEK_SET
-
SetПозиция ввода-вывода с начала - WRITABLE
-
Маска событий записи для
IO#wait.
Методы публичного класса
static VALUE
rb_io_s_binread(int argc, VALUE *argv, VALUE io)
{
VALUE offset;
struct foreach_arg arg;
enum {
fmode = FMODE_READABLE|FMODE_BINMODE,
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.
При вызове из класса IO (но не подклассов IO), этот метод имеет потенциальные уязвимости в области безопасности, если вызывается с недоверенными данными; см. Внедрение кода.
static VALUE
rb_io_s_binwrite(int argc, VALUE *argv, VALUE io)
{
return io_s_write(argc, argv, io, 1);
} Ведет себя как IO.write, за исключением того, что поток открывается в двоичном режиме с кодировкой ASCII-8BIT.
При вызове из класса IO (но не подклассов IO), этот метод имеет потенциальные уязвимости в области безопасности, если вызывается с недоверенными данными; см. Внедрение кода.
static VALUE
console_dev(int argc, VALUE *argv, VALUE klass)
{
VALUE con = 0;
VALUE sym = 0;
rb_check_arity(argc, 0, UNLIMITED_ARGUMENTS);
if (argc) {
Check_Type(sym = argv[0], T_SYMBOL);
}
// Force the class to be File.
if (klass == rb_cIO) klass = rb_cFile;
if (rb_const_defined(klass, id_console)) {
con = rb_const_get(klass, id_console);
if (!RB_TYPE_P(con, T_FILE) || RTEST(rb_io_closed_p(con))) {
rb_const_remove(klass, id_console);
con = 0;
}
}
if (sym) {
if (sym == ID2SYM(id_close) && argc == 1) {
if (con) {
rb_io_close(con);
rb_const_remove(klass, id_console);
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;
rb_io_t *ofptr;
#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
rb_const_set(klass, id_console, con);
}
if (sym) {
return rb_f_send(argc, argv, con);
}
return con;
} Возвращает экземпляр File, открытый в консоли.
Если sym задан, он будет отправлен в открытую консоль с args, и результат будет возвращен вместо объекта консоли IO самого.
Для использования этого метода необходимо подключить ‘io/console’.
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);
} Вызывает блок с каждой последующей строкой, считанной из потока.
При вызове из класса IO (но не подклассов 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;
rb_io_t *fp;
int fd, fmode, oflags = O_RDONLY;
struct rb_io_encoding convconfig;
VALUE opt;
#if defined(HAVE_FCNTL) && defined(F_GETFL)
int ofmode;
#else
struct stat st;
#endif
argc = rb_scan_args(argc, argv, "11:", &fnum, &vmode, &opt);
rb_io_extract_modeenc(&vmode, 0, opt, &oflags, &fmode, &convconfig);
fd = NUM2INT(fnum);
if (rb_reserved_fd_p(fd)) {
rb_raise(rb_eArgError, "The given fd is not accessible because RubyVM reserves it");
}
#if defined(HAVE_FCNTL) && defined(F_GETFL)
oflags = fcntl(fd, F_GETFL);
if (oflags == -1) rb_sys_fail(0);
#else
if (fstat(fd, &st) < 0) rb_sys_fail(0);
#endif
rb_update_max_fd(fd);
#if defined(HAVE_FCNTL) && defined(F_GETFL)
ofmode = rb_io_oflags_fmode(oflags);
if (NIL_P(vmode)) {
fmode = ofmode;
}
else if ((~ofmode & fmode) & FMODE_READWRITE) {
VALUE error = INT2FIX(EINVAL);
rb_exc_raise(rb_class_new_instance(1, &error, rb_eSystemCallError));
}
#endif
VALUE path = Qnil;
if (!NIL_P(opt)) {
if (rb_hash_aref(opt, sym_autoclose) == Qfalse) {
fmode |= FMODE_EXTERNAL;
}
path = rb_hash_aref(opt, RB_ID2SYM(idPath));
if (!NIL_P(path)) {
StringValue(path);
path = rb_str_new_frozen(path);
}
}
MakeOpenFile(io, fp);
fp->self = io;
fp->fd = fd;
fp->mode = fmode;
fp->encs = convconfig;
fp->pathv = path;
fp->timeout = Qnil;
clear_codeconv(fp);
io_check_tty(fp);
if (fileno(stdin) == fd)
fp->stdio_file = stdin;
else if (fileno(stdout) == fd)
fp->stdio_file = stdout;
else if (fileno(stderr) == fd)
fp->stdio_file = stderr;
if (fmode & FMODE_SETENC_BY_BOM) io_set_encoding_by_bom(io);
return io;
} Создаёт и возвращает новый объект 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;
int 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"
Необязательные ключевые аргументы mode задают:
-
Параметры для
Kernel#spawn.
Разветвлённый процесс
Когда аргумент cmd является строкой длиной 1 символ '-', заставляет процесс разветвиться:
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 если байты не были считан.
При вызове из класса IO (но не подклассов IO), этот метод имеет потенциальные уязвимости безопасности, если вызывается с ненадежным вводом; см. Инъекция команд.
Первый аргумент должен быть строкой, представляющей путь к файлу.
Только с аргументом 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);
} Возвращает массив всех строк, считанных из потока.
При вызове из класса IO (но не подклассов 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)) {
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 представляет собой целое число, интервал таймаута в секундах.
Метод отслеживает объекты ввода-вывода, заданные во всех трех массивах, ожидая, пока некоторые из них не подготовятся; возвращает массив из 3 элементов, которые:
-
Массив объектов из
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, сокеты pipe и сокеты, когда несколько процессов читают из потока.
Наконец, разработчики ядра 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 следующим образом: IO::WaitReadable также следует обрабатывать при переподключении SSL в OpenSSL::SSL::SSLSocket.
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 {
SafeStringValue(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 в него и закрывает поток; возвращает количество записанных байтов.
При вызове из класса IO (но не подклассов IO), этот метод имеет потенциальные уязвимости в безопасности, если вызывается с ненадежным вводом; см. Внедрение команд.
Первый аргумент должен быть строкой, представляющей путь к файлу.
При использовании только аргумента 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;
} 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 в противном случае. См. Режим данных.
_WIN32
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;
} static VALUE
console_clear_screen(VALUE io)
{
console_erase_screen(io, INT2FIX(2));
console_goto(io, INT2FIX(0), INT2FIX(0));
return io;
} 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.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;
} Устанавливает флаг close-on-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 устанавливает флаги close-on-exec для всех дескрипторов файлов по умолчанию, начиная с Ruby 2.0.0. Поэтому вам не нужно устанавливать его самостоятельно. Кроме того, сброс флага close-on-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 в режиме cooked.
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;
} Включает режим cooked.
Если режим терминала необходимо вернуть обратно, используйте 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), 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));
} static VALUE
console_cursor_down(VALUE io, VALUE val)
{
return console_move(io, +NUM2INT(val), 0);
} static VALUE
console_cursor_left(VALUE io, VALUE val)
{
return console_move(io, 0, -NUM2INT(val));
} static VALUE
console_cursor_right(VALUE io, VALUE val)
{
return console_move(io, 0, +NUM2INT(val));
} static VALUE
console_cursor_up(VALUE io, VALUE val)
{
return console_move(io, -NUM2INT(val), 0);
} 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);
if (NEED_READCONV(fptr)) {
SET_BINARY_MODE(fptr);
r = 1; /* no invalid char yet */
for (;;) {
make_readconv(fptr, 0);
for (;;) {
if (fptr->cbuf.len) {
if (fptr->encs.enc)
r = rb_enc_precise_mbclen(fptr->cbuf.ptr+fptr->cbuf.off,
fptr->cbuf.ptr+fptr->cbuf.off+fptr->cbuf.len,
fptr->encs.enc);
else
r = ONIGENC_CONSTRUCT_MBCLEN_CHARFOUND(1);
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)) {
enc = fptr->encs.enc;
goto invalid;
}
return io;
}
}
if (MBCLEN_INVALID_P(r)) {
enc = fptr->encs.enc;
goto invalid;
}
n = MBCLEN_CHARFOUND_LEN(r);
if (fptr->encs.enc) {
c = rb_enc_codepoint(fptr->cbuf.ptr+fptr->cbuf.off,
fptr->cbuf.ptr+fptr->cbuf.off+fptr->cbuf.len,
fptr->encs.enc);
}
else {
c = (unsigned char)fptr->cbuf.ptr[fptr->cbuf.off];
}
fptr->cbuf.off += n;
fptr->cbuf.len -= n;
rb_yield(UINT2NUM(c));
rb_io_check_byte_readable(fptr);
}
}
NEED_NEWLINE_DECORATOR_ON_READ_CHECK(fptr);
enc = io_input_encoding(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 объединяет оба поведения:
-
Вызывает с следующей строкой, определяемой разделителем строк
sep. -
Но возвращает не более байтов, чем разрешено пределом.
Необязательный ключевой аргумент 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 в противном случае; см. Position:
f = File.open('t.txt')
f.eof # => false
f.seek(0, :END) # => 0
f.eof # => true
f.close
Вызывает исключение, если поток не открыт для чтения; см. Mode.
Если self является потоком, таким как pipe или socket, этот метод блокируется, пока другой конец не отправит данные или не закроет его:
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("\x1b[%dK", mode));
#endif
return io;
} 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("\x1b[%dJ", mode));
#endif
return io;
} # 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.
При вызове без блока ожидает, пока входные данные, соответствующие заданному pattern, не будут получены из IO или истечёт время, указанное в качестве тайм-аута. Массив возвращается, когда шаблон получен из IO. Первый элемент массива — это вся строка, полученная из IO до совпадения шаблона, за которой следуют элементы, указывающие, какой шаблон соответствовал якорю в регулярном выражении.
Необязательный параметр timeout определяет в секундах общее время ожидания шаблона. Если тайм-аут истекает или найден eof, возвращается или передаётся 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));
} Возвращает объект Encoding, представляющий кодировку потока, или nil, если поток находится в режиме записи и кодировка не указана.
См. Encodings.
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_thread_io_blocking_region(nogvl_fdatasync, fptr, fptr->fd) == 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_thread_io_blocking_region(nogvl_fsync, fptr, fptr->fd) < 0)
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, если уже достигнут конец потока. См. Byte IO.
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);
} Считывает и возвращает следующую строку из 1 символа из потока; возвращает nil, если уже достигнут конец потока. См. Character IO.
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 объединяет оба поведения:
-
Возвращает следующую строку, определяемую разделителем строк
sep, илиnil, если разделителя нет. -
Но возвращает не более байтов, чем разрешено пределом.
Необязательный ключевой аргумент 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
COORD pos;
int fd = GetWriteFD(io);
pos.X = NUM2UINT(x);
pos.Y = NUM2UINT(y);
if (!SetConsoleCursorPosition((HANDLE)rb_w32_get_osfhandle(fd), pos)) {
rb_syserr_fail(LAST_ERROR, 0);
}
#else
rb_io_write(io, rb_sprintf("\x1b[%d;%dH", NUM2UINT(y)+1, NUM2UINT(x)+1));
#endif
return io;
} 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("\x1b[%dG", NUM2UINT(val)+1));
#endif
return io;
} 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));
} Возвращает объект Encoding, представляющий кодировку внутреннего строкового значения, если указано преобразование, или nil в противном случае.
См. Кодировки.
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/STDER в режиме без ожидания. (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
io_nread(VALUE io)
{
rb_io_t *fptr;
int len;
ioctl_arg n;
GetOpenFile(io, fptr);
rb_io_check_readable(fptr);
len = rb_io_read_pending(fptr);
if (len > 0) return INT2FIX(len);
#ifdef HAVE_RB_IO_DESCRIPTOR
int fd = rb_io_descriptor(io);
#else
int fd = fptr->fd;
#endif
if (!FIONREAD_POSSIBLE_P(fd)) return INT2FIX(0);
if (ioctl(fd, FIONREAD, &n)) return INT2FIX(0);
if (n > 0) return ioctl_arg2num(n);
return INT2FIX(0);
} Возвращает количество байтов, которые могут быть прочитаны без ожидания. Возвращает ноль, если информация недоступна.
Для использования этого метода необходимо загрузить ‘io/wait’.
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);
} Перемещает указатель на указанную позицию (в байтах); см. Позиция:
f = File.open('t.txt')
f.tell # => 0
f.pos = 20 # => 20
f.tell # => 20
f.close
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 = {.io = io};
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.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, за исключением:
-
Чтение в указанной позиции (в байтах).
-
Игнорирование и не изменение текущей позиции потока (см. Позиция).
-
Пропускание любого кеширования в пользовательском пространстве потока.
Поскольку этот метод не изменяет состояние потока (в частности, его позицию), 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
Вывод:
$\ = "\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 = {.io = io};
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.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)rb_thread_io_blocking_call(internal_pwrite_func, &arg, fptr->fd, RB_WAITFD_OUT);
if (n < 0) rb_sys_fail_path(fptr->pathv);
rb_str_tmp_frozen_release(str, tmp);
return SSIZET2NUM(n);
} Поведение аналогично IO#write, за исключением:
-
Запись в указанной позиции (в байтах).
-
Игнорирование и не изменение текущей позиции потока (см. Позиция).
-
Пропускание любого кеширования в пользовательском пространстве потока.
Поскольку этот метод не изменяет состояние потока (в частности, его позицию), 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 в сыром режиме, и возвращает результат блока.
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;
} Включает сырой режим и возвращает 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. Таким образом, IO::WaitReadable можно использовать для перехвата исключений и повторной попытки read_nonblock.
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
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 объединяются оба поведения:
-
Возвращает строки, определяемые разделителем строк
sep. -
Но возвращает не более байтов в строке, чем разрешено пределом.
Необязательный ключевой аргумент 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 вызовом read, readpartial повторно выполняет системный вызов.
Последнее означает, что readpartial нечувствителен к флагу «без ожидания». Он блокируется в ситуации, когда IO#sysread вызывает Errno::EWOULDBLOCK так, как если бы fd был в режиме блокировки.
Примеры:
# # 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
io_ready_p(VALUE io)
{
rb_io_t *fptr;
#ifndef HAVE_RB_IO_WAIT
struct timeval tv = {0, 0};
#endif
GetOpenFile(io, fptr);
rb_io_check_readable(fptr);
if (rb_io_read_pending(fptr)) return Qtrue;
#ifndef HAVE_RB_IO_WAIT
return wait_for_single_fd(fptr, RB_WAITFD_IN, &tv) ? Qtrue : Qfalse;
#else
return io_wait_event(io, RUBY_IO_READABLE, RB_INT2NUM(0), 1);
#endif
} Возвращает значение «истина», если входные данные доступны без блокировки, или значение «ложь».
Для использования этого метода требуется «io/wait».
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)) {
int 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));
} static VALUE
console_scroll_forward(VALUE io, VALUE val)
{
return console_scroll(io, +NUM2INT(val));
} 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 или строкой с именем кодировки; он назначается как кодировка для потока.
Аргумент int_enc, если задан, должен быть объектом Encoding или строкой с именем кодировки; он назначается как кодировка для внутренней строки.
Аргумент '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 (маркер порядка байтов), маркер игнорируется, и внешняя кодировка устанавливается соответствующим образом; возвращает результат кодировки, если найдена, или 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
Вызывает исключение, если поток не находится в binmode или его кодировка уже установлена.
static VALUE
rb_io_stat(VALUE obj)
{
rb_io_t *fptr;
struct stat st;
GetOpenFile(obj, fptr);
if (fstat(fptr->fd, &st) == -1) {
rb_sys_fail_path(fptr->pathv);
}
return rb_stat_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);
} Возвращает текущий режим синхронизации потока. Если режим синхронизации равен true, все вывод немедленно передаётся в операционную систему и не буферизуется 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 в self, который должен быть открыт для записи (см. Режимы); возвращает количество записанных байтов. Если 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.
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:
SafeStringValue(b);
}
io_ungetbyte(b, fptr);
return Qnil;
} Возвращает nil. Возвращает nil. Возвращает 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 {
SafeStringValue(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. Возвращает 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)
{
#ifndef HAVE_RB_IO_WAIT
rb_io_t *fptr;
struct timeval timerec;
struct timeval *tv = NULL;
int event = 0;
int i;
GetOpenFile(io, fptr);
for (i = 0; i < argc; ++i) {
if (SYMBOL_P(argv[i])) {
event |= wait_mode_sym(argv[i]);
}
else {
*(tv = &timerec) = rb_time_interval(argv[i]);
}
}
/* rb_time_interval() and might_mode() might convert the argument */
rb_io_check_closed(fptr);
if (!event) event = RB_WAITFD_IN;
if ((event & RB_WAITFD_IN) && rb_io_read_pending(fptr))
return Qtrue;
if (wait_for_single_fd(fptr, event, tv))
return io;
return Qnil;
#else
VALUE timeout = Qundef;
rb_io_event_t events = 0;
int i, return_io = 0;
/* The documented signature for this method is actually incorrect.
* A single timeout is allowed in any position, and multiple symbols can be given.
* Whether this is intentional or not, I don't know, and as such I consider this to
* be a legacy/slow path. */
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 (i = 0; i < argc; i += 1) {
if (RB_SYMBOL_P(argv[i])) {
events |= wait_mode_sym(argv[i]);
}
else if (timeout == Qundef) {
rb_time_interval(timeout = argv[i]);
}
else {
rb_raise(rb_eArgError, "timeout given more than once");
}
}
if (timeout == Qundef) 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);
#endif
} Ожидает, пока IO не станет готовым для указанных событий и возвращает подмножество событий, которые стали готовыми, или ложное значение, когда истекает время ожидания.
События могут быть битовой маской IO::READABLE, IO::WRITABLE или IO::PRIORITY.
Возвращает истинное значение немедленно, когда доступны данные из буфера.
Необязательный параметр mode — это :read, :write или :read_write.
Для использования этого метода необходимо подключить «io/wait».
static VALUE
io_wait_priority(int argc, VALUE *argv, VALUE io)
{
rb_io_t *fptr = NULL;
RB_IO_POINTER(io, fptr);
rb_io_check_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 и, как правило, ограничены потоками.
Для использования этого метода необходимо подключить «io/wait».
static VALUE
io_wait_readable(int argc, VALUE *argv, VALUE io)
{
rb_io_t *fptr;
#ifndef HAVE_RB_IO_WAIT
struct timeval timerec;
struct timeval *tv;
#endif
GetOpenFile(io, fptr);
rb_io_check_readable(fptr);
#ifndef HAVE_RB_IO_WAIT
tv = get_timeout(argc, argv, &timerec);
#endif
if (rb_io_read_pending(fptr)) return Qtrue;
#ifndef HAVE_RB_IO_WAIT
if (wait_for_single_fd(fptr, RB_WAITFD_IN, tv)) {
return io;
}
return Qnil;
#else
rb_check_arity(argc, 0, 1);
VALUE timeout = (argc == 1 ? argv[0] : Qnil);
return io_wait_event(io, RUBY_IO_READABLE, timeout, 1);
#endif
} Ожидает, пока IO станет доступным для чтения и возвращает значение «истина», или значение «ложь» при истечении времени ожидания. Возвращает значение «истина» немедленно, если в буфере есть данные.
Для использования этого метода необходимо загрузить «io/wait».
static VALUE
io_wait_writable(int argc, VALUE *argv, VALUE io)
{
rb_io_t *fptr;
#ifndef HAVE_RB_IO_WAIT
struct timeval timerec;
struct timeval *tv;
#endif
GetOpenFile(io, fptr);
rb_io_check_writable(fptr);
#ifndef HAVE_RB_IO_WAIT
tv = get_timeout(argc, argv, &timerec);
if (wait_for_single_fd(fptr, RB_WAITFD_OUT, tv)) {
return io;
}
return Qnil;
#else
rb_check_arity(argc, 0, 1);
VALUE timeout = (argc == 1 ? argv[0] : Qnil);
return io_wait_event(io, RUBY_IO_WRITABLE, timeout, 1);
#endif
} Ожидает, пока IO станет доступным для записи и возвращает значение «истина», или значение «ложь» при истечении времени ожидания.
Для использования этого метода необходимо загрузить «io/wait».
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;
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");
}
ws.dwSize.X = newcol;
ret = SetConsoleScreenBufferSize(wh, ws.dwSize);
ws.srWindow.Left = 0;
ws.srWindow.Top = 0;
ws.srWindow.Right = newcol-1;
ws.srWindow.Bottom = newrow-1;
if (!SetConsoleWindowInfo(wh, TRUE, &ws.srWindow)) {
rb_syserr_fail(LAST_ERROR, "SetConsoleWindowInfo");
}
/* retry when shrinking buffer after shrunk window */
if (!ret && !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. Таким образом, IO::WaitWritable может использоваться для перехвата исключений для повторной попытки write_nonblock.
# 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–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.