класс IO
Библиотека Expect добавляет метод экземпляра IO expect, который выполняет действия, аналогичные расширению expect в tcl.
Для использования этого метода необходимо подключить expect:
require 'expect'
Обратитесь к expect для получения информации об использовании.
Класс IO является основой для всех операций ввода-вывода в Ruby. Поток ввода-вывода может быть дуплексным (т. е. двунаправленным) и может использовать более одного потока операционной системы.
Многие примеры в этом разделе используют класс File, единственный стандартный подкласс IO. Эти два класса тесно связаны. Как и класс File, библиотека Socket наследуется от IO (например, TCPSocket или UDPSocket).
Метод Kernel#open может создать объект IO (или File) для таких аргументов:
-
Простая строка представляет имя файла, подходящее для операционной системы.
-
Строка, начинающаяся с
"|", указывает на дочерний процесс. Остаток строки после"|"запускается как процесс с соответствующими каналами ввода/вывода, подключенными к нему. -
Строка, равная
"|-", создаст другой экземпляр Ruby как дочерний процесс.
Поток IO может быть открыт с различными режимами файлов (только для чтения, только для записи) и кодировками для правильного преобразования. См. IO.new для этих параметров. См. Kernel#open для получения подробной информации о различных форматах команд, описанных выше.
IO.popen, библиотека Open3 или Process#spawn также могут использоваться для связи с дочерними процессами через IO.
Ruby будет преобразовывать пути между различными соглашениями об именах файлов разных операционных систем, если это возможно. Например, в системе Windows имя файла "/gumby/ruby/test.rb" будет открыто как "\gumby\ruby\test.rb". При указании имени файла в стиле Windows в строке Ruby не забудьте экранировать обратные косые черты:
"C:\\gumby\\ruby\\test.rb"
В наших примерах здесь будут использоваться косые черты в стиле Unix; File::ALT_SEPARATOR можно использовать для получения символа разделителя, специфичного для платформы.
Глобальная константа ARGF (также доступна как $<) предоставляет поток, похожий на IO, который позволяет получить доступ ко всем файлам, упомянутым в командной строке (или STDIN, если не указаны файлы). ARGF#path и его псевдоним ARGF#filename предназначены для доступа к имени файла, который в данный момент читается.
io/console
Расширение io/console предоставляет методы для взаимодействия с консолью. К консоли можно получить доступ из IO.console или стандартных объектов ввода/вывода/ошибок IO.
Подключение io/console добавляет следующие методы:
Пример:
require 'io/console'
rows, columns = $stdout.winsize
puts "Your screen is #{columns} wide and #{rows} tall"
Константы
- EWOULDBLOCKWaitReadable
- EWOULDBLOCKWaitWritable
- PRIORITY
- READABLE
- SEEK_CUR
-
SetПоложение ввода-вывода от текущего положения - SEEK_DATA
-
SetПоложение ввода-вывода до следующей области содержащей данные - SEEK_END
-
SetПоложение ввода-вывода от конца - SEEK_HOLE
-
SetПоложение ввода-вывода до следующей области без данных - SEEK_SET
-
SetПоложение ввода-вывода от начала - WRITABLE
Методы публичного класса
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
};
convconfig_t 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);
} Открывает файл, необязательно переходит к указанному offset, затем возвращает length байт (по умолчанию, остаток файла). binread гарантирует, что файл закрыт перед возвращением. Режим открытия будет "rb:ASCII-8BIT".
IO.binread("testfile") #=> "This is line one\nThis is line two\nThis is line three\nAnd so on...\n"
IO.binread("testfile", 20) #=> "This is line one\nThi"
IO.binread("testfile", 20, 10) #=> "ne one\nThis is line "
static VALUE
rb_io_s_binwrite(int argc, VALUE *argv, VALUE io)
{
return io_s_write(argc, argv, io, 1);
} Аналогично IO.write, за исключением открытия файла в двоичном режиме и кодировке ASCII-8BIT ("wb:ASCII-8BIT").
static VALUE
console_dev(int argc, VALUE *argv, VALUE klass)
{
VALUE con = 0;
rb_io_t *fptr;
VALUE sym = 0;
rb_check_arity(argc, 0, UNLIMITED_ARGUMENTS);
if (argc) {
Check_Type(sym = argv[0], T_SYMBOL);
}
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) ||
(!(fptr = RFILE(con)->fptr) || GetReadFD(fptr) == -1)) {
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) {
VALUE args[2];
#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;
#ifdef CONSOLE_DEVICE_FOR_WRITING
fd = rb_cloexec_open(CONSOLE_DEVICE_FOR_WRITING, O_RDWR, 0);
if (fd < 0) return Qnil;
rb_update_max_fd(fd);
args[1] = INT2FIX(O_WRONLY);
args[0] = INT2NUM(fd);
out = rb_class_new_instance(2, args, klass);
#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;
}
rb_update_max_fd(fd);
args[1] = INT2FIX(O_RDWR);
args[0] = INT2NUM(fd);
con = rb_class_new_instance(2, args, klass);
GetOpenFile(con, fptr);
fptr->pathv = rb_obj_freeze(rb_str_new2(CONSOLE_DEVICE));
#ifdef CONSOLE_DEVICE_FOR_WRITING
GetOpenFile(out, ofptr);
ofptr->pathv = fptr->pathv;
fptr->tied_io_for_writing = out;
ofptr->mode |= FMODE_SYNC;
#endif
fptr->mode |= FMODE_SYNC;
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;
if (NIL_P(length))
st.copy_length = (off_t)-1;
else
st.copy_length = NUM2OFFT(length);
if (NIL_P(src_offset))
st.src_offset = (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);
} IO.copy_stream копирует src в dst. src и dst — это либо имя файла, либо объект, подобный объекту ввода-вывода. Объект, подобный объекту ввода-вывода для src, должен иметь метод readpartial или read. Объект, подобный объекту ввода-вывода для dst, должен иметь метод write. (В соответствующих ситуациях могут использоваться специализированные механизмы, такие как системный вызов sendfile.)
Этот метод возвращает количество скопированных байтов.
Если необязательные аргументы не указаны, начальная позиция копирования — начало имени файла или текущая позиция файла объекта IO. Конечная позиция копирования — конец файла.
Если задан copy_length, то не более copy_length байтов будут скопированы.
Если задан src_offset, он указывает начальную позицию копирования.
Когда задан src_offset и src — объект IO, IO.copy_stream не перемещает текущую позицию файла.
# File ext/io/console/lib/console/size.rb, line 3
def IO.default_console_size
[
ENV["LINES"].to_i.nonzero? || 25,
ENV["COLUMNS"].to_i.nonzero? || 80,
]
end Возвращает размер окна консоли по умолчанию
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, "13:", NULL, 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);
} Выполняет блок для каждой строки в указанном порту ввода-вывода, где строки разделены sep.
Если блок не задан, возвращается перечислитель.
IO.foreach("testfile") {|x| print "GOT ", x }
результат:
GOT This is line one GOT This is line two GOT This is line three GOT And so on...
Если последний аргумент — хэш, это ключевые аргументы для открытия. См. IO.readlines для получения дополнительной информации об аргументах getline_args. Также см. IO.read для получения дополнительной информации об аргументах open_args.
static VALUE
rb_io_initialize(int argc, VALUE *argv, VALUE io)
{
VALUE fnum, vmode;
rb_io_t *fp;
int fd, fmode, oflags = O_RDONLY;
convconfig_t 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
if (!NIL_P(opt) && rb_hash_aref(opt, sym_autoclose) == Qfalse) {
fmode |= FMODE_PREP;
}
MakeOpenFile(io, fp);
fp->self = io;
fp->fd = fd;
fp->mode = fmode;
fp->encs = convconfig;
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 (поток) для заданного целочисленного дескриптора файла fd и mode строки. opt может использоваться для более удобочитаемого указания частей mode. Также см. IO.sysopen и IO.for_fd.
IO.new вызывается различными методами открытия File и IO, такими как IO::open, Kernel#open и File::open.
Режим открытия
Когда mode является целым числом, оно должно быть комбинацией режимов, определённых в File::Constants (File::RDONLY, File::WRONLY|File::CREAT). Подробнее см. страницу руководства open(2).
Когда mode является строкой, она должна быть в одном из следующих форматов:
fmode fmode ":" ext_enc fmode ":" ext_enc ":" int_enc fmode ":" "BOM|UTF-*"
fmode — это строка режима открытия IO, ext_enc — внешнее кодирование для IO, а int_enc — внутреннее кодирование.
IO Режим открытия
Ruby допускает следующие режимы открытия:
"r" Read-only, starts at beginning of file (default mode).
"r+" Read-write, starts at beginning of file.
"w" Write-only, truncates existing file
to zero length or creates a new file for writing.
"w+" Read-write, truncates existing file to zero length
or creates a new file for reading and writing.
"a" Write-only, each write call appends data at end of file.
Creates a new file for writing if file does not exist.
"a+" Read-write, each write call appends data at end of file.
Creates a new file for reading and writing if file does
not exist. Следующие режимы должны использоваться по отдельности и вместе с одним или несколькими режимами, указанными выше.
"b" Binary file mode
Suppresses EOL <-> CRLF conversion on Windows. And
sets external encoding to ASCII-8BIT unless explicitly
specified.
"t" Text file mode Режим эксклюзивного доступа (“x”) может использоваться вместе с “w”, чтобы гарантировать создание файла. Если файл уже существует, генерируется исключение Errno::EEXIST. Он может не поддерживаться для всех типов потоков (например, для каналов).
Если режим открытия исходного IO — только для чтения, его нельзя изменить на режим записи. Аналогично, режим открытия нельзя изменить с только для записи на режим чтения.
При попытке такого изменения ошибка генерируется в разных местах в зависимости от платформы.
IO Encoding
Когда ext_enc указан, строки, считанные при чтении, будут помечены кодировкой, а строки, выводимые при записи, будут преобразованы в указанную кодировку.
Когда ext_enc и int_enc указаны, считываемые строки будут преобразованы из ext_enc в int_enc при вводе, а записываемые строки — из int_enc в ext_enc при выводе. См. Encoding для получения дополнительных сведений о преобразовании при вводе и выводе.
Если используются “BOM|UTF-8”, “BOM|UTF-16LE” или “BOM|UTF16-BE”, Ruby проверяет наличие Unicode BOM в входном документе, чтобы определить кодировку. Для UTF-16 кодировок режим открытия файла должен быть двоичным. При наличии BOM он удаляется, и внешнее кодирование из BOM используется. Если BOM отсутствует, указанная кодировка Unicode используется как ext_enc. (Вариант кодировки с BOM нечувствителен к регистру, поэтому «bom|utf-8» также допустим.)
Параметры
opt может быть использован вместо mode для лучшей читаемости. Поддерживаются следующие ключи:
- :mode
-
То же, что и параметр
mode - :flags
-
Указывает флаги открытия файла в виде целого числа. Если указан параметр
mode, этот параметр будет побитово суммирован. - :external_encoding
-
Внешнее кодирование для
IO. - :internal_encoding
-
Внутреннее кодирование для
IO. «-» — синоним для значения по умолчанию внутреннего кодирования.Если значение равно
nil, преобразование не происходит. - :encoding
-
Указывает внешнее и внутреннее кодирования как «extern:intern».
- :textmode
-
Если значение имеет истинное значение, то же самое, что и «t» в аргументе
mode. - :binmode
-
Если значение имеет истинное значение, то же самое, что и «b» в аргументе
mode. - :autoclose
-
Если значение
false, тоfdбудет оставаться открытым после завершения работы экземпляраIO.
Кроме того, opt может иметь те же ключи, что и в String#encode, для управления преобразованием между внешним и внутренним кодированиями.
Пример 1
fd = IO.sysopen("/dev/tty", "w")
a = IO.new(fd,"w")
$stderr.puts "Hello"
a.puts "World"
Результат:
Hello World
Пример 2
require 'fcntl'
fd = STDERR.fcntl(Fcntl::F_DUPFD)
io = IO.new(fd, mode: 'w:UTF-16LE', cr_newline: true)
io.puts "Hello, World!"
fd = STDERR.fcntl(Fcntl::F_DUPFD)
io = IO.new(fd, mode: 'w', cr_newline: true,
external_encoding: Encoding::UTF_16LE)
io.puts "Hello, World!"
Оба примера выводят «Привет, мир!» в UTF-16LE на стандартный поток ошибок, конвертируя EOL, сгенерированные puts, в CR.
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.open — это синоним для IO.new. Если дан необязательный блок кода, он будет передан io в качестве аргумента, и объект IO будет автоматически закрыт по окончании блока. В этом случае IO.open возвращает значение блока.
Подробное описание параметров IO.new, fd и mode см. в IO.new.
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) && v1 == Qnil) {
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 defined(RUBY_TEST_CRLF_ENVIRONMENT) || defined(_WIN32)
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;
} Создаёт пару конечных точек канала (связанных друг с другом) и возвращает их в виде массива из двух элементов объектов IO: [ read_io, write_io ].
Если задан блок, вызывается блок, и возвращается значение блока. read_io и write_io передаются в блок в качестве аргументов. Если read_io и write_io не закрыты при выходе из блока, они закрываются. То есть закрытие read_io и/или write_io не вызывает ошибку.
Не доступно на всех платформах.
Если в качестве необязательного аргумента задана кодировка (имя кодировки или объект кодировки), то считанная из канала строка будет помечена указанной кодировкой. Если аргумент представляет собой две строки кодировок, разделённые двоеточием «A:B», то считанная строка преобразуется из кодировки A (внешняя кодировка) в кодировку B (внутренняя кодировка), а затем помечается кодировкой B. Если заданы два необязательных аргумента, они должны быть объектами кодировок или именами кодировок, причём первый — внешнее кодирование, а второй — внутреннее. Если указаны внешнее и внутреннее кодирование, необязательный аргумент хэш определяет параметры преобразования.
В примере ниже два процесса закрывают концы канала, которые они не используют. Это не просто косметический элемент. Конец канала для чтения не будет генерировать состояние конца файла, если какие-либо записывающие процессы с каналом всё ещё открыты. В случае родительского процесса 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);
} Выполняет указанную команду как дочерний процесс; стандартный ввод и вывод дочернего процесса будут подключены к возвращаемому объекту IO.
ИД процесса запущенного процесса можно получить с помощью метода IO#pid.
cmd — это строка или массив, как следует из этого:
cmd: "-" : fork commandline : command line string which is passed to a shell [env, cmdname, arg1, ..., opts] : command name and zero or more arguments (no shell) [env, [cmdname, argv0], arg1, ..., opts] : command name, argv[0] and zero or more arguments (no shell) (env and opts are optional.)
Если cmd — это String “-”, то в качестве дочернего процесса запускается новая копия Ruby.
Если cmd — это Array из String, то он будет использоваться как argv дочернего процесса, минуя оболочку. Массив может содержать хэш в начале для окружения и хэш в конце для опций, аналогично spawn.
По умолчанию режим для нового объекта файла — “r”, но mode может быть установлен на любой из режимов, перечисленных в описании для класса IO. Последний аргумент opt уточняет mode.
# 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
# spawn option. See the document of Kernel.spawn.
IO.popen(["ls", "/", :err=>[:child, :out]]) {|ls_io|
ls_result_with_error = ls_io.read
}
# spawn options can be mixed with IO options
IO.popen(["ls", "/"], :err=>[:child, :out]) {|ls_io|
ls_result_with_error = ls_io.read
}
Возбуждает исключения, которые возбуждаются методами IO.pipe и Kernel.spawn.
Если задан блок, Ruby выполнит команду как дочерний процесс, подключенный к Ruby с помощью канала. Конец канала Ruby будет передан в качестве параметра блоку. По завершении блока Ruby закроет канал и установит $?. В этом случае IO.popen возвращает значение блока.
Если блок задан с cmd «-», блок будет выполнен в двух отдельных процессах: один в родительском процессе, и один в дочернем. Родительский процесс получит объект канала в качестве параметра блока, дочерний вариант блока получит nil, и стандартный ввод и стандартный вывод дочернего процесса будут подключены к родительскому через канал. Недоступно на всех платформах.
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;
static VALUE
rb_io_s_read(int argc, VALUE *argv, VALUE io)
{
VALUE opt, offset;
struct foreach_arg arg;
argc = rb_scan_args(argc, argv, "13:", NULL, NULL, &offset, NULL, &opt);
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);
} Открывает файл, необязательно переходит к указанной offset, а затем возвращает length байтов (по умолчанию — остаток файла). read гарантирует, что файл закрывается перед возвратом.
Если name начинается с символа канала ("|"), создается дочерний процесс так же, как и в Kernel#open, и его вывод возвращается.
Параметры
Хэш параметров принимает следующие ключи:
- :encoding
-
строка или кодировка
Указывает кодировку считанной строки.
:encodingбудет проигнорирован, если указанlength. См.Encoding.aliasesдля возможных кодировок. - :mode
-
строка или целое число
Указывает аргумент mode для open(). Он должен начинаться с “r”, иначе это вызовет ошибку. См.
IO.newдля списка возможных режимов. - :open_args
-
массив
Указывает аргументы для open() в виде массива. Этот ключ не может использоваться в сочетании с
:encodingили:mode.
Примеры:
IO.read("testfile") #=> "This is line one\nThis is line two\nThis is line three\nAnd so on...\n"
IO.read("testfile", 20) #=> "This is line one\nThi"
IO.read("testfile", 20, 10) #=> "ne one\nThis is line "
IO.read("binfile", mode: "rb") #=> "\xF7\x00\x00\x0E\x12"
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, "13:", NULL, 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);
} Читает весь файл, указанный name, как отдельные строки и возвращает эти строки в массиве. Строки разделяются с помощью sep.
a = IO.readlines("testfile")
a[0] #=> "This is line one\n"
b = IO.readlines("testfile", chomp: true)
b[0] #=> "This is line one"
Если последний аргумент — хэш, это ключевой аргумент для open.
Параметры для getline
Хэш параметров принимает следующие ключи:
- :chomp
-
Если необязательный ключевой аргумент
chompимеет истинное значение,\n,\r, и\r\nбудут удалены с конца каждой строки.
См. также IO.read для получения дополнительной информации об open_args.
static VALUE
rb_f_select(int argc, VALUE *argv, VALUE obj)
{
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). Он отслеживает заданные массивы объектов IO, ожидает, пока один или несколько объектов IO будут готовы к чтению, готовы к записи и имеют ожидающие исключения соответственно, и возвращает массив, содержащий массивы этих объектов IO. Он вернёт nil если необязательное значение timeout задано и ни один объект IO не готов в течение timeout секунд.
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 или socket, когда несколько процессов читают из потока.
Наконец, разработчики ядра Linux не гарантируют, что возможность чтения select(2) означает возможность чтения последующего read(2), даже для одного процесса. См. руководство select(2) на системе GNU/Linux.
Вызов 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
Параметры
- read_array
-
массив объектов
IO, ожидающих готовности к чтению - write_array
-
массив объектов
IO, ожидающих готовности к записи - error_array
-
массив объектов
IO, ожидающих исключений - timeout
-
числовое значение во секундах
Пример
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);
} Открывает заданный путь, возвращая базовый дескриптор файла в виде Integer.
IO.sysopen("testfile") #=> 3
static VALUE
rb_io_s_try_convert(VALUE dummy, VALUE io)
{
return rb_io_check_io(io);
} Попытка преобразовать obj в IO, используя метод to_io. Возвращает преобразованный объект IO или nil , если obj не может быть преобразован по какой-либо причине.
IO.try_convert(STDOUT) #=> STDOUT
IO.try_convert("STDOUT") #=> nil
require 'zlib'
f = open("/tmp/zz.gz") #=> #<File:/tmp/zz.gz>
z = Zlib::GzipReader.open(f) #=> #<Zlib::GzipReader:0x81d8744>
IO.try_convert(z) #=> #<File:/tmp/zz.gz>
static VALUE
rb_io_s_write(int argc, VALUE *argv, VALUE io)
{
return io_s_write(argc, argv, io, 0);
} Открывает файл, при необходимости перемещает указатель к заданному offset, записывает string, затем возвращает длину записанных данных. write гарантирует, что файл закрыт перед возвратом. Если offset не указан в режиме записи, файл обрезается. В противном случае он не обрезается.
IO.write("testfile", "0123456789", 20) #=> 10
# File could contain: "This is line one\nThi0123456789two\nThis is line three\nAnd so on...\n"
IO.write("testfile", "0123456789") #=> 10
# File would now read: "0123456789"
Если последний аргумент — хеш, он определяет опции для внутреннего open(). Он принимает следующие ключи:
- :encoding
-
строка или кодировка
Указывает кодировку считываемой строки. См.
Encoding.aliasesдля возможных кодировок. - :mode
-
строка или целое число
Указывает аргумент mode для open(). Он должен начинаться с «w», «a» или «r+», в противном случае это вызовет ошибку. См.
IO.newдля списка возможных режимов. - :perm
-
целое число
Указывает аргумент perm для open().
- :open_args
-
массив
Указывает аргументы для open() в виде массива. Этот ключ не может использоваться в сочетании с другими ключами.
Публичные методы экземпляра
VALUE
rb_io_addstr(VALUE io, VALUE str)
{
rb_io_write(io, str);
return io;
} String Вывод—Записывает obj в ios. obj будет преобразован в строку с помощью to_s.
$stdout << "Hello " << "world!\n"
результат:
Hello world!
static VALUE
rb_io_advise(int argc, VALUE *argv, VALUE io)
{
VALUE advice, offset, len;
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_fadvise(2), этот метод ничего не делает.
advice — один из следующих символов:
- :normal
-
Нет рекомендаций; предположение по умолчанию для открытого файла.
- :sequential
-
К данным будет осуществляться последовательный доступ с чтением меньших смещений перед большими.
- :random
-
К данным будет осуществляться доступ в произвольном порядке.
- :willneed
-
К данным будет осуществляться доступ в ближайшем будущем.
- :dontneed
-
К данным не будет осуществляться доступ в ближайшем будущем.
- :noreuse
-
К данным будет осуществляться доступ только один раз.
Семантика рекомендации зависит от платформы. Подробнее см. man 2 posix_fadvise.
«данные» означают область текущего файла, которая начинается со смещения offset и продолжается на len байт. Если len равно 0, область заканчивается на последнем байте файла. По умолчанию offset и len равны 0, что означает, что рекомендация применяется ко всему файлу.
В случае ошибки будет возбуждено одно из следующих исключений:
-
IOError -
Поток
IOзакрыт. - Errno::EBADF
-
Дескриптор файла текущего файла недействителен.
- Errno::EINVAL
-
Указано недопустимое значение для advice.
- Errno::ESPIPE
-
Дескриптор файла текущего файла ссылается на FIFO или канал. (Linux в этом случае вызывает Errno::EINVAL).
-
TypeError -
Либо advice не был
Symbol, либо один из других аргументов не былInteger. -
RangeError -
Один из переданных аргументов слишком большой/маленький.
- Этот список не является исчерпывающим; также возможны другие исключения
Errno -
исключения.
static VALUE
rb_io_set_autoclose(VALUE io, VALUE autoclose)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
if (!RTEST(autoclose))
fptr->mode |= FMODE_PREP;
else
fptr->mode &= ~FMODE_PREP;
return autoclose;
} Устанавливает флаг автоматического закрытия.
f = open("/dev/null")
IO.for_fd(f.fileno)
# ...
f.gets # may cause Errno::EBADF
f = open("/dev/null")
IO.for_fd(f.fileno).autoclose = false
# ...
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 (fptr->mode & FMODE_PREP) ? Qfalse : Qtrue;
} Возвращает true если базовый дескриптор файла ios будет автоматически закрыт при его завершении, в противном случае false.
static VALUE
console_beep(VALUE io)
{
rb_io_t *fptr;
int fd;
GetOpenFile(io, fptr);
fd = GetWriteFD(fptr);
#ifdef _WIN32
(void)fd;
MessageBeep(0);
#else
if (write(fd, "\a", 1) < 0)
sys_fail_fptr(fptr);
#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;
} Переводит ios в двоичный режим. После того, как поток переведен в двоичный режим, его нельзя вернуть в недвоичный режим.
-
преобразование символов новой строки отключено
-
преобразование кодировки отключено
-
содержимое обрабатывается как ASCII-8BIT
static VALUE
rb_io_binmode_p(VALUE io)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
return fptr->mode & FMODE_BINMODE ? Qtrue : Qfalse;
} Возвращает true если ios находится в binmode.
static VALUE
console_check_winsize_changed(VALUE io)
{
rb_io_t *fptr;
HANDLE h;
DWORD num;
GetOpenFile(io, fptr);
h = (HANDLE)rb_w32_get_osfhandle(GetReadFD(fptr));
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;
} Закрывает ios и очищает все ожидающие записи в операционную систему. Поток недоступен для каких-либо дальнейших операций с данными; если будет предпринята такая попытка, будет возбуждено исключение IOError. Потоки ввода-вывода автоматически закрываются, когда они захватываются сборщиком мусора.
Если ios открыт с помощью IO.popen, close устанавливает $?.
Вызов этого метода для закрытого объекта IO просто игнорируется, начиная с Ruby 2.3.
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 = open("/dev/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 если ios будет закрыт при exec.
f = open("/dev/null")
f.close_on_exec? #=> false
f.close_on_exec = true
f.close_on_exec? #=> true
f.close_on_exec = false
f.close_on_exec? #=> false
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);
} Закрывает входной конец дуплексного потока ввода-вывода (т. е. потока, содержащего как входной, так и выходной поток, например, канал). Возбуждает исключение IOError, если поток не является дуплексным.
f = IO.popen("/bin/sh","r+")
f.close_read
f.readlines
результат:
prog.rb:3:in `readlines': not opened for reading (IOError) from prog.rb:3
Вызов этого метода для закрытого объекта IO просто игнорируется, начиная с Ruby 2.3.
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;
} Закрывает выходной конец дуплексного потока ввода-вывода (т. е. потока, содержащего как входной, так и выходной поток, например, канал). Возбуждает исключение IOError, если поток не является дуплексным.
f = IO.popen("/bin/sh","r+")
f.close_write
f.print "nowhere"
результат:
prog.rb:3:in `write': not opened for writing (IOError) from prog.rb:3:in `print' from prog.rb:3
Вызов этого метода для закрытого объекта IO просто игнорируется, начиная с Ruby 2.3.
static VALUE
rb_io_closed(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 0 <= fptr->fd ? Qfalse : Qtrue;
} Возвращает true если ios полностью закрыт (для дуплексных потоков как reader, так и writer), false в противном случае.
f = File.new("testfile")
f.close #=> nil
f.closed? #=> true
f = IO.popen("/bin/sh","r+")
f.close_write #=> nil
f.closed? #=> false
f.close_read #=> nil
f.closed? #=> true
static VALUE
console_conmode_get(VALUE io)
{
conmode t;
rb_io_t *fptr;
int fd;
GetOpenFile(io, fptr);
fd = GetReadFD(fptr);
if (!getattr(fd, &t)) sys_fail_fptr(fptr);
return conmode_new(cConmode, &t);
} Возвращает данные, представляющие текущий режим консоли.
Для использования этого метода необходимо require 'io/console'.
static VALUE
console_conmode_set(VALUE io, VALUE mode)
{
conmode *t, r;
rb_io_t *fptr;
int fd;
TypedData_Get_Struct(mode, conmode, &conmode_type, t);
r = *t;
GetOpenFile(io, fptr);
fd = GetReadFD(fptr);
if (!setattr(fd, &r)) sys_fail_fptr(fptr);
return mode;
} Устанавливает режим консоли в mode.
Для использования этого метода необходимо require '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;
rb_io_t *fptr;
int fd;
GetOpenFile(io, fptr);
fd = GetReadFD(fptr);
if (!getattr(fd, &t)) sys_fail_fptr(fptr);
set_cookedmode(&t, NULL);
if (!setattr(fd, &t)) sys_fail_fptr(fptr);
return io;
} Включает режим cooked.
Если нужно вернуть режим терминала, используйте io.cooked { … }.
Для использования этого метода необходимо подключить 'io/console'.
static VALUE
console_cursor_pos(VALUE io)
{
rb_io_t *fptr;
int fd;
rb_console_size_t ws;
GetOpenFile(io, fptr);
fd = GetWriteFD(fptr);
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));
} 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_line(int argc, VALUE *argv, VALUE io)
{
VALUE str;
struct getline_arg args;
RETURN_ENUMERATOR(io, argc, argv);
prepare_getline_args(argc, argv, &args, io);
if (args.limit == 0)
rb_raise(rb_eArgError, "invalid limit: 0 for each_line");
while (!NIL_P(str = rb_io_getline_1(args.rs, args.limit, args.chomp, io))) {
rb_yield(str);
}
return io;
} Выполняет блок для каждой строки в ios, где строки разделены sep. ios должен быть открыт для чтения, иначе будет поднято исключение IOError.
Если блок не задан, вместо этого возвращается перечислитель.
f = File.new("testfile")
f.each {|line| puts "#{f.lineno}: #{line}" }
вывод:
1: This is line one 2: This is line two 3: This is line three 4: And so on...
См. IO.readlines для получения подробностей о getline_args.
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));
errno = 0;
}
rb_io_check_byte_readable(fptr);
READ_CHECK(fptr);
} while (io_fillbuf(fptr) >= 0);
return io;
} Вызывает данный блок один раз для каждого байта (0..255) в ios, передавая байт в качестве аргумента. Поток должен быть открыт для чтения, иначе будет поднято исключение IOError.
Если блок не задан, вместо этого возвращается перечислитель.
f = File.new("testfile")
checksum = 0
f.each_byte {|x| checksum ^= x } #=> #<File:testfile>
checksum #=> 12
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;
} Вызывает данный блок один раз для каждого символа в ios, передавая символ в качестве аргумента. Поток должен быть открыт для чтения, иначе будет поднято исключение IOError.
Если блок не задан, вместо этого возвращается перечислитель.
f = File.new("testfile")
f.each_char {|c| print c, ' ' } #=> #<File:testfile>
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));
}
}
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;
}
}
return io;
invalid:
rb_raise(rb_eArgError, "invalid byte sequence in %s", rb_enc_name(enc));
UNREACHABLE_RETURN(Qundef);
} Передает Integer порядковый номер каждого символа в ios, передавая код символа в качестве аргумента. Поток должен быть открыт для чтения, иначе будет поднято исключение IOError.
Если блок не задан, вместо этого возвращается перечислитель.
Выполняет блок для каждой строки в ios, где строки разделены sep. ios должен быть открыт для чтения, иначе будет поднято исключение IOError.
Если блок не задан, вместо этого возвращается перечислитель.
f = File.new("testfile")
f.each {|line| puts "#{f.lineno}: #{line}" }
вывод:
1: This is line one 2: This is line two 3: This is line three 4: And so on...
См. IO.readlines для получения подробностей о getline_args.
static VALUE
console_set_echo(VALUE io, VALUE f)
{
conmode t;
rb_io_t *fptr;
int fd;
GetOpenFile(io, fptr);
fd = GetReadFD(fptr);
if (!getattr(fd, &t)) sys_fail_fptr(fptr);
if (RTEST(f))
set_echo(&t, NULL);
else
set_noecho(&t, NULL);
if (!setattr(fd, &t)) sys_fail_fptr(fptr);
return io;
} Включает/отключает эхо-возврат. На некоторых платформах все комбинации этих флагов и режимов raw/cooked могут быть невалидными.
Для использования этого метода необходимо подключить 'io/console'.
static VALUE
console_echo_p(VALUE io)
{
conmode t;
rb_io_t *fptr;
int fd;
GetOpenFile(io, fptr);
fd = GetReadFD(fptr);
if (!getattr(fd, &t)) sys_fail_fptr(fptr);
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 defined(RUBY_TEST_CRLF_ENVIRONMENT) || defined(_WIN32)
if (!NEED_READCONV(fptr) && NEED_NEWLINE_DECORATOR_ON_READ(fptr)) {
return eof(fptr->fd) ? Qtrue : Qfalse;
}
#endif
if (io_fillbuf(fptr) < 0) {
return Qtrue;
}
return Qfalse;
} Возвращает true, если ios достиг конца файла, то есть больше нет данных для чтения. Поток должен быть открыт для чтения, иначе будет поднято исключение IOError.
f = File.new("testfile")
dummy = f.readlines
f.eof #=> true
Если ios – это поток, такой как pipe или сокет, IO#eof? блокируется до тех пор, пока другой конец не отправит данные или не закроется.
r, w = IO.pipe
Thread.new { sleep 1; w.close }
r.eof? #=> true after 1 second blocking
r, w = IO.pipe
Thread.new { sleep 1; w.puts "a" }
r.eof? #=> false after 1 second blocking
r, w = IO.pipe
r.eof? # blocks forever
Обратите внимание, что IO#eof? считывает данные в буфер входных байтов. Поэтому IO#sysread может не работать так, как ожидается, с IO#eof?, если вы не вызовете IO#rewind сначала (что недоступно для некоторых потоков).
Возвращает true, если ios достиг конца файла, то есть больше нет данных для чтения. Поток должен быть открыт для чтения, иначе будет поднято исключение IOError.
f = File.new("testfile")
dummy = f.readlines
f.eof #=> true
Если ios – это поток, такой как pipe или сокет, IO#eof? блокируется до тех пор, пока другой конец не отправит данные или не закроется.
r, w = IO.pipe
Thread.new { sleep 1; w.close }
r.eof? #=> true after 1 second blocking
r, w = IO.pipe
Thread.new { sleep 1; w.puts "a" }
r.eof? #=> false after 1 second blocking
r, w = IO.pipe
r.eof? # blocks forever
Обратите внимание, что IO#eof? считывает данные в буфер входных байтов. Поэтому IO#sysread может не работать так, как ожидается, с IO#eof?, если вы не вызовете IO#rewind сначала (что недоступно для некоторых потоков).
static VALUE
console_erase_line(VALUE io, VALUE val)
{
rb_io_t *fptr;
HANDLE h;
rb_console_size_t ws;
COORD *pos = &ws.dwCursorPosition;
DWORD w;
int mode = mode_in_range(val, 2, "line erase");
GetOpenFile(io, fptr);
h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(fptr));
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;
} static VALUE
console_erase_screen(VALUE io, VALUE val)
{
rb_io_t *fptr;
HANDLE h;
rb_console_size_t ws;
COORD *pos = &ws.dwCursorPosition;
DWORD w;
int mode = mode_in_range(val, 3, "screen erase");
GetOpenFile(io, fptr);
h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(fptr));
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);
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 Считывает из IO до тех пор, пока не будет соответствия заданному pattern или не истечёт timeout.
Возвращает массив со считанным буфером, за которым следуют совпадения. Если передан блок, результат передаётся в блок, и возвращается nil.
При вызове без блока ожидание продолжается, пока входные данные, соответствующие заданному pattern , не будут получены из IO, или не истечёт указанное время ожидания. Массив возвращается, когда паттерн получен из IO. Первый элемент массива — вся строка, полученная из IO до совпадения с шаблоном, за которым следуют элементы, указывающие, какой шаблон сопоставился с якорем в регулярном выражении.
Необязательный параметр timeout определяет общее время ожидания шаблона в секундах. Если истечёт срок ожидания или будет достигнут конец файла (eof), возвращается или передаётся nil. Однако буфер в сессии ожидания сохраняется для следующего вызова expect. Значение по умолчанию для параметра timeout — 9999999 секунд.
static VALUE
rb_io_external_encoding(VALUE io)
{
rb_io_t *fptr;
GetOpenFile(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, представляющий кодировку файла. Если io находится в режиме записи и кодировка не указана, возвращает nil.
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);
} Предоставляет механизм для выполнения команд низкого уровня для управления или запроса потоков ввода-вывода, ориентированных на файлы. Аргументы и результаты зависят от платформы. Если arg — число, его значение передаётся непосредственно. Если это строка, она интерпретируется как двоичная последовательность байтов (Array#pack может быть полезным способом создания этой строки). На платформах Unix см. fcntl(2) для получения подробной информации. Не реализовано на всех платформах.
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);
} Немедленно записывает все данные буфера в ios на диск.
Если платформа не поддерживает fdatasync(2), вызывается IO#fsync (что может вызвать NotImplementedError).
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);
} Возвращает целое число, представляющее числовой дескриптор файла для ios.
$stdin.fileno #=> 0 $stdout.fileno #=> 1
VALUE
rb_io_flush(VALUE io)
{
return rb_io_flush_raw(io, 1);
} Очищает все буферизованные данные в ios в операционной системе (обратите внимание, что это только внутренняя буферизация Ruby; ОС также может буферизовать данные).
$stdout.print "no newline" $stdout.flush
результат:
no newline
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);
} Немедленно записывает все буферизованные данные в ios на диск. Обратите внимание, что fsync отличается от использования IO#sync=. Последний гарантирует очистку данных из буферов Ruby, но не гарантирует, что операционная система фактически запишет их на диск.
Если платформа не поддерживает fsync(2), генерируется NotImplementedError.
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);
} Возвращает следующий 8-битный байт (0..255) из ios. Возвращает nil , если вызов сделан в конце файла.
f = File.new("testfile")
f.getbyte #=> 84
f.getbyte #=> 104
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);
} Читает строку длиной в один символ из ios. Возвращает nil , если вызов сделан в конце файла.
f = File.new("testfile")
f.getc #=> "h"
f.getc #=> "e"
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_scheduler_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, RUBY_IO_READABLE, timeout);
if (result == Qfalse) 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 для получения подробной информации о параметрах.
Для использования этого метода необходимо выполнить require '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);
str = rb_ensure(getpass_call, io, puts_call, wio);
return str_chomp(str);
} Считывает и возвращает строку без отображения ввода. Выводит prompt , если не nil.
Для использования этого метода необходимо выполнить require 'io/console'.
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;
} Читает следующую «строку» из потока ввода-вывода; строки разделены символом sep. Разделитель nil считывает всё содержимое, а разделитель нулевой длины считывает входные данные по абзацам (два последовательных перевода строки во входных данных разделяют абзацы). Поток должен быть открыт для чтения, в противном случае будет возбуждено исключение IOError. Считанная строка возвращается и присваивается $_. Возвращает nil , если вызов сделан в конце файла. Если первый аргумент — целое число или передан необязательный второй аргумент, возвращаемая строка не будет длиннее заданного значения в байтах.
File.new("testfile").gets #=> "This is line one\n"
$_ #=> "This is line one\n"
File.new("testfile").gets(4)#=> "This"
Если IO содержит многобайтовые символы, то gets(1) возвращает весь символ целиком:
# Russian characters take 2 bytes
File.write("testfile", "\u{442 435 441 442}")
File.open("testfile") {|f|f.gets(1)} #=> "\u0442"
File.open("testfile") {|f|f.gets(2)} #=> "\u0442"
File.open("testfile") {|f|f.gets(3)} #=> "\u0442\u0435"
File.open("testfile") {|f|f.gets(4)} #=> "\u0442\u0435"
static VALUE
console_goto(VALUE io, VALUE y, VALUE x)
{
rb_io_t *fptr;
int fd;
COORD pos;
GetOpenFile(io, fptr);
fd = GetWriteFD(fptr);
pos.X = NUM2UINT(x);
pos.Y = NUM2UINT(y);
if (!SetConsoleCursorPosition((HANDLE)rb_w32_get_osfhandle(fd), pos)) {
rb_syserr_fail(LAST_ERROR, 0);
}
return io;
} static VALUE
console_goto_column(VALUE io, VALUE val)
{
rb_io_t *fptr;
HANDLE h;
rb_console_size_t ws;
COORD *pos = &ws.dwCursorPosition;
GetOpenFile(io, fptr);
h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(fptr));
if (!GetConsoleScreenBufferInfo(h, &ws)) {
rb_syserr_fail(LAST_ERROR, 0);
}
pos->X = NUM2INT(val);
if (!SetConsoleCursorPosition(h, *pos)) {
rb_syserr_fail(LAST_ERROR, 0);
}
return io;
} static VALUE
console_iflush(VALUE io)
{
rb_io_t *fptr;
int fd;
GetOpenFile(io, fptr);
fd = GetReadFD(fptr);
#if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
if (tcflush(fd, TCIFLUSH)) sys_fail_fptr(fptr);
#endif
(void)fd;
return io;
} Очищает буфер ввода в ядре.
Для использования этого метода необходимо выполнить require '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, ">");
} Возвращает строку, описывающую этот объект IO.
static VALUE
rb_io_internal_encoding(VALUE io)
{
rb_io_t *fptr;
GetOpenFile(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);
} Предоставляет механизм для выдачи команд низкого уровня для управления или запроса устройств ввода-вывода. Аргументы и результаты зависят от платформы. Если arg — число, его значение передаётся непосредственно. Если это строка, она интерпретируется как двоичная последовательность байтов. На платформах Unix см. ioctl(2) для получения подробной информации. Не реализовано на всех платформах.
static VALUE
console_ioflush(VALUE io)
{
rb_io_t *fptr;
#if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
int fd1, fd2;
#endif
GetOpenFile(io, fptr);
#if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
fd1 = GetReadFD(fptr);
fd2 = GetWriteFD(fptr);
if (fd2 != -1 && fd1 != fd2) {
if (tcflush(fd1, TCIFLUSH)) sys_fail_fptr(fptr);
if (tcflush(fd2, TCOFLUSH)) sys_fail_fptr(fptr);
}
else {
if (tcflush(fd1, TCIOFLUSH)) sys_fail_fptr(fptr);
}
#endif
return io;
} Сбрасывает буферы ввода и вывода в ядре.
Необходимо подключить 'io/console', чтобы использовать этот метод.
static VALUE
rb_io_isatty(VALUE io)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
if (isatty(fptr->fd) == 0)
return Qfalse;
return Qtrue;
} Возвращает true, если ios связан с терминальным устройством (tty), false в противном случае.
File.new("testfile").isatty #=> false
File.new("/dev/tty").isatty #=> true
static VALUE
rb_io_lineno(VALUE io)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
rb_io_check_char_readable(fptr);
return INT2NUM(fptr->lineno);
} Возвращает текущий номер строки в ios. Поток должен быть открыт для чтения. lineno считает количество вызовов gets, а не количество встреченных символов новой строки. Эти два значения будут различаться, если gets вызывается с разделителем, отличным от символа новой строки.
Методы, использующие $/ например, each, lines и readline также будут увеличивать lineno.
См. также переменную $..
f = File.new("testfile")
f.lineno #=> 0
f.gets #=> "This is line one\n"
f.lineno #=> 1
f.gets #=> "This is line two\n"
f.lineno #=> 2
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;
} Вручную устанавливает текущий номер строки на заданное значение. $. обновляется только при следующем чтении.
f = File.new("testfile")
f.gets #=> "This is line one\n"
$. #=> 1
f.lineno = 1000
f.lineno #=> 1000
$. #=> 1 # lineno of last read
f.gets #=> "This is line two\n"
$. #=> 1001 # lineno of last read
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 io)
{
int nb = 1;
rb_io_t *fptr;
int f, restore[2];
GetOpenFile(io, fptr);
if (argc > 0) {
VALUE v;
rb_scan_args(argc, argv, "01", &v);
nb = RTEST(v);
}
f = io_nonblock_mode(fptr->fd);
restore[0] = fptr->fd;
restore[1] = f;
if (!io_nonblock_set(fptr->fd, f, nb))
return rb_yield(io);
return rb_ensure(rb_yield, io, io_nonblock_restore, (VALUE)restore);
} Передает self в неблокирующем режиме.
Когда false задан в качестве аргумента, self передается в блокирующем режиме. Исходный режим восстанавливается после выполнения блока.
static VALUE
rb_io_nonblock_set(VALUE io, VALUE nb)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
if (RTEST(nb))
rb_io_set_nonblock(fptr);
else
io_nonblock_set(fptr->fd, io_nonblock_mode(fptr->fd), RTEST(nb));
return io;
} Включает неблокирующий режим в потоке, когда установлен в true, и блокирующий режим, когда установлен в false.
static VALUE
rb_io_nonblock_p(VALUE io)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
if (io_nonblock_mode(fptr->fd) & O_NONBLOCK)
return Qtrue;
return Qfalse;
} Возвращает true, если объект IO находится в неблокирующем режиме.
static VALUE
io_nread(VALUE io)
{
rb_io_t *fptr = NULL;
ioctl_arg n;
GetOpenFile(io, fptr);
rb_io_check_readable(fptr);
int len = rb_io_read_pending(fptr);
if (len > 0) return INT2FIX(len);
if (!FIONREAD_POSSIBLE_P(fptr->fd)) return INT2FIX(0);
if (ioctl(fptr->fd, FIONREAD, &n)) return INT2FIX(0);
if (n > 0) return ioctl_arg2num(n);
return INT2FIX(0);
} Возвращает количество байтов, которые можно прочитать без блокировки. Возвращает ноль, если информация недоступна.
static VALUE
console_oflush(VALUE io)
{
rb_io_t *fptr;
int fd;
GetOpenFile(io, fptr);
fd = GetWriteFD(fptr);
#if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
if (tcflush(fd, TCOFLUSH)) sys_fail_fptr(fptr);
#endif
(void)fd;
return io;
} Сбрасывает буфер вывода в ядре.
Необходимо подключить 'io/console', чтобы использовать этот метод.
static VALUE
io_pathconf(VALUE io, VALUE arg)
{
int name;
long ret;
rb_io_t *fptr;
name = NUM2INT(arg);
GetOpenFile(io, fptr);
errno = 0;
ret = fpathconf(fptr->fd, 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);
} Возвращает идентификатор процесса дочернего процесса, связанного с ios. Он будет установлен с помощью 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
Возвращает текущий смещение (в байтах) ios.
f = File.new("testfile")
f.pos #=> 0
f.gets #=> "This is line one\n"
f.pos #=> 17
static VALUE
rb_io_set_pos(VALUE io, VALUE offset)
{
rb_io_t *fptr;
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);
} Перемещается на заданную позицию (в байтах) в ios. Не гарантируется, что переход на правильную позицию, когда ios находится в текстовом режиме.
f = File.new("testfile")
f.pos = 17
f.gets #=> "This is line two\n"
static VALUE
rb_io_pread(int argc, VALUE *argv, VALUE io)
{
VALUE len, offset, str;
rb_io_t *fptr;
ssize_t n;
struct prdwr_internal_arg arg;
int shrinkable;
rb_scan_args(argc, argv, "21", &len, &offset, &str);
arg.count = NUM2SIZET(len);
arg.offset = NUM2OFFT(offset);
shrinkable = io_setstrbuf(&str, (long)arg.count);
if (arg.count == 0) return str;
arg.buf = RSTRING_PTR(str);
GetOpenFile(io, fptr);
rb_io_check_byte_readable(fptr);
arg.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;
} Читает maxlen байтов из ios используя системный вызов pread и возвращает их в виде строки, не изменяя смещение базового дескриптора. Это выгодно по сравнению с сочетанием IO#seek и IO#read тем, что оно атомарно, позволяя нескольким потокам/процессам совместно использовать один и тот же объект IO для чтения файла в разных местах. Это обходит любое буферизацию в пользовательском пространстве уровня IO. Если присутствует необязательный аргумент outbuf, он должен ссылаться на String, который получит данные. Вызывает SystemCallError при ошибке, EOFError в конце файла и NotImplementedError, если платформа не реализует системный вызов.
File.write("testfile", "This is line one\nThis is line two\n")
File.open("testfile") do |f|
p f.read # => "This is line one\nThis is line two\n"
p f.pread(12, 0) # => "This is line"
p f.pread(9, 8) # => "line one\n"
end
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;
} Записывает заданный объект(ы) в ios. Возвращает nil.
Поток должен быть открыт для записи. Каждый заданный объект, который не является строкой, будет преобразован путем вызова его метода to_s. При вызове без аргументов выводит содержимое $_.
Если разделитель выходного поля ($,) не nil, он вставляется между объектами. Если разделитель выходной записи ($\) не nil, он добавляется к выводу.
$stdout.print("This is ", 100, " percent.\n")
выводит:
This is 100 percent.
VALUE
rb_io_printf(int argc, const VALUE *argv, VALUE out)
{
rb_io_write(out, rb_f_sprintf(argc, argv));
return Qnil;
} Форматирует и записывает в ios, преобразуя параметры под управлением строки формата. См. Kernel#sprintf для подробностей.
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;
} Если obj является Numeric, записывает символ, код которого является младшим байтом obj. Если obj является String, записывает первый символ obj в ios. В противном случае, вызывает TypeError.
$stdout.putc "A" $stdout.putc 65
выводит:
AA
VALUE
rb_io_puts(int argc, const VALUE *argv, VALUE out)
{
int i, n;
VALUE line, args[2];
/* if no argument given, print newline. */
if (argc == 0) {
rb_io_write(out, rb_default_rs);
return Qnil;
}
for (i=0; i<argc; i++) {
if (RB_TYPE_P(argv[i], T_STRING)) {
line = argv[i];
goto string;
}
if (rb_exec_recursive(io_puts_ary, argv[i], out)) {
continue;
}
line = rb_obj_as_string(argv[i]);
string:
n = 0;
args[n++] = line;
if (RSTRING_LEN(line) == 0 ||
!rb_str_end_with_asciichar(line, '\n')) {
args[n++] = rb_default_rs;
}
rb_io_writev(out, n, args);
}
return Qnil;
} Записывает заданный объект(ы) в ios. Записывает перевод строки после любого объекта, который не заканчивается последовательностью перевода строки. Возвращает nil.
Поток должен быть открыт для записи. Если вызывается с аргументом типа массив, записывает каждый элемент с новой строки. Каждый заданный объект, который не является строкой или массивом, будет преобразован путём вызова его метода to_s. Если вызывается без аргументов, выводит одну перевод строки.
$stdout.puts("this", "is", ["a", "test"])
выводит:
this is a test
Обратите внимание, что puts всегда использует переводы строк и не зависит от разделителя записи вывода ($\).
static VALUE
rb_io_pwrite(VALUE io, VALUE str, VALUE offset)
{
rb_io_t *fptr;
ssize_t n;
struct prdwr_internal_arg arg;
VALUE tmp;
if (!RB_TYPE_P(str, T_STRING))
str = rb_obj_as_string(str);
arg.offset = NUM2OFFT(offset);
io = GetWriteIO(io);
GetOpenFile(io, fptr);
rb_io_check_writable(fptr);
arg.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_region(internal_pwrite_func, &arg, fptr->fd);
if (n < 0) rb_sys_fail_path(fptr->pathv);
rb_str_tmp_frozen_release(str, tmp);
return SSIZET2NUM(n);
} Записывает заданную строку в ios по offset с помощью системного вызова pwrite(). Это выгодно по сравнению с комбинированием IO#seek и IO#write, так как оно является атомарным, позволяя нескольким потокам/процессам совместно использовать тот же объект IO для чтения файла в разных местах. Это обходит любые буферизацию в пользовательском пространстве слоя IO. Возвращает количество записанных байтов. Вызывает SystemCallError при ошибке и NotImplementedError, если платформа не реализует системный вызов.
File.open("out", "w") do |f|
f.pwrite("ABCDEF", 3) #=> 6
end
File.read("out") #=> "\u0000\u0000\u0000ABCDEF"
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, включает специальные символы для прерывания, выхода и приостановки.
Дополнительные сведения см. в руководстве по termios.
Для использования этого метода необходимо подключить 'io/console'.
static VALUE
console_set_raw(int argc, VALUE *argv, VALUE io)
{
conmode t;
rb_io_t *fptr;
int fd;
rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts);
GetOpenFile(io, fptr);
fd = GetReadFD(fptr);
if (!getattr(fd, &t)) sys_fail_fptr(fptr);
set_rawmode(&t, optp);
if (!setattr(fd, &t)) sys_fail_fptr(fptr);
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 defined(RUBY_TEST_CRLF_ENVIRONMENT) || defined(_WIN32)
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 defined(RUBY_TEST_CRLF_ENVIRONMENT) || defined(_WIN32)
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 defined(RUBY_TEST_CRLF_ENVIRONMENT) || defined(_WIN32)
if (previous_mode == O_TEXT) {
setmode(fptr->fd, O_TEXT);
}
#endif
if (n == 0) return Qnil;
return str;
} Считывает length байтов из потока ввода/вывода.
length должен быть неотрицательным целым числом или nil.
Если length — положительное целое число, read пытается прочитать length байтов без преобразования (двоичный режим). Возвращает nil, если EOF обнаружен до чтения чего-либо. Возвращаются меньше чем length байтов, если EOF обнаружен во время чтения. В случае целого числа length, результирующая строка всегда находится в кодировке ASCII-8BIT.
Если length опущен или равен nil, он считывает до EOF, и применяется преобразование кодировки, если применимо. Строка возвращается даже если EOF обнаружен до чтения каких-либо данных.
Если length равно нулю, возвращает пустую строку ("").
Если присутствует необязательный аргумент outbuf, он должен ссылаться на String, который получит данные. outbuf будет содержать только полученные данные после вызова метода, даже если он не был пустым изначально.
При вызове этого метода в конце файла, он возвращает nil или "", в зависимости от length: read, read(nil), и read(0) возвращают "", read(positive_integer) возвращает nil.
f = File.new("testfile")
f.read(16) #=> "This is line one"
# read whole file
open("file") do |f|
data = f.read # This returns a string even if the file is empty.
# ...
end
# iterate over fixed length records
open("fixed-record-file") do |f|
while record = f.read(256)
# ...
end
end
# iterate over variable length records,
# each record is prefixed by its 32-bit length
open("variable-record-file") do |f|
while len = f.read(4)
len = len.unpack("N")[0] # 32-bit length
record = f.read(len) # This returns a string even if len is 0.
end
end
Обратите внимание, что этот метод ведет себя как функция fread() в C. Это означает, что он повторно пытается вызвать системные вызовы read(2) для чтения данных с указанной длиной (или до EOF). Это поведение сохраняется, даже если ios находится в режиме без ожидания. (Этот метод нечувствителен к флагу без ожидания, как и другие методы.) Если вам нужно поведение, подобное одиночному системному вызову read(2), рассмотрите readpartial, read_nonblock и sysread.
# 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;
} Считывает байт так же, как и с 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;
} Считывает строку длиной в один символ из ios. Вызывает EOFError при достижении конца файла.
f = File.new("testfile")
f.readchar #=> "h"
f.readchar #=> "e"
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);
} Считывает все строки в ios и возвращает их в виде массива. Строки разделяются необязательным разделителем sep. Если sep является nil, остаток потока возвращается как одна запись. Если первый аргумент является целым числом или задан необязательный второй аргумент, возвращаемая строка не будет длиннее заданного значения в байтах. Поток должен быть открыт для чтения, иначе будет вызвано исключение IOError.
f = File.new("testfile")
f.readlines[0] #=> "This is line one\n"
f = File.new("testfile", chomp: true)
f.readlines[0] #=> "This is line one"
См. IO.readlines для получения подробной информации о getline_args.
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 байтов из потока ввода-вывода. Блокируется только в том случае, если в ios нет данных, доступных немедленно. Не блокируется, если данные доступны.
Если присутствует необязательный аргумент outbuf, он должен ссылаться на String, который получит данные. outbuf будет содержать только полученные данные после вызова метода, даже если он не пуст в начале.
Вызывает исключение EOFError при окончании файла.
readpartial предназначен для таких потоков, как pipe, socket, tty и т. д. Он блокируется только тогда, когда данные недоступны немедленно. Это означает, что он блокируется только тогда, когда выполняются все следующие условия.
-
буфер байтов в объекте
IOпуст. -
содержимое потока пусто.
-
поток не достиг конца файла (EOF).
Когда readpartial блокируется, он ожидает данные или EOF в потоке. Если данные получены, readpartial возвращает данные. Если достигнут EOF, readpartial вызывает исключение EOFError.
Когда readpartial не блокируется, он возвращает значение или вызывает исключение немедленно. Если буфер байтов не пуст, он возвращает данные из буфера. В противном случае, если поток содержит некоторые данные, он возвращает данные из потока. В противном случае, если поток достиг EOF, он вызывает исключение EOFError.
r, w = IO.pipe # buffer pipe content w << "abc" # "" "abc". r.readpartial(4096) #=> "abc" "" "" r.readpartial(4096) # blocks because buffer and pipe is empty. r, w = IO.pipe # buffer pipe content w << "abc" # "" "abc" w.close # "" "abc" EOF r.readpartial(4096) #=> "abc" "" EOF r.readpartial(4096) # raises EOFError r, w = IO.pipe # buffer pipe content 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" "" ""
Обратите внимание, что readpartial ведет себя аналогично sysread. Различия заключаются в следующем:
-
Если буфер байтов не пуст, чтение выполняется из буфера байтов вместо «sysread для буферизованного
IO(IOError)». -
Он не вызывает Errno::EWOULDBLOCK и Errno::EINTR. Когда readpartial встречает EWOULDBLOCK и EINTR при вызове системного чтения, readpartial повторяет вызов системы.
Последнее означает, что readpartial нечувствителен к флагу без блокировки. Он блокируется в ситуации, когда IO#sysread вызывает Errno::EWOULDBLOCK, как если бы fd был в режиме блокировки.
static VALUE
io_ready_p(VALUE io)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
rb_io_check_readable(fptr);
if (rb_io_read_pending(fptr)) return Qtrue;
return io_wait_event(io, RUBY_IO_READABLE, RB_INT2NUM(0));
} Возвращает true, если ввод доступен без блокировки, или false.
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;
convconfig_t convconfig;
rb_io_extract_modeenc(&nmode, 0, opt, &oflags, &fmode, &convconfig);
if (IS_PREP_STDIO(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;
} Повторно связывает ios с потоком ввода-вывода, указанным в other_IO, или с новым потоком, открытым по path. Это может динамически изменить фактический класс этого потока. Параметры mode и opt принимают такие же значения, как IO.open.
f1 = File.new("testfile")
f2 = File.new("testfile")
f2.readlines[0] #=> "This is line one\n"
f2.reopen(f1) #=> #<File:testfile>
f2.readlines[0] #=> "This is line one\n"
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);
} Устанавливает позицию ios в начало ввода, сбрасывая lineno в ноль.
f = File.new("testfile")
f.readline #=> "This is line one\n"
f.rewind #=> 0
f.lineno #=> 0
f.readline #=> "This is line one\n"
Обратите внимание, что он не может использоваться с такими потоками, как каналы, терминалы и сокеты.
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);
} Перемещает указатель на заданный смещение anInteger в потоке в соответствии со значением whence:
:CUR or IO::SEEK_CUR | Seeks to _amount_ plus current position
----------------------+--------------------------------------------------
:END or IO::SEEK_END | Seeks to _amount_ plus end of stream (you
| probably want a negative value for _amount_)
----------------------+--------------------------------------------------
:SET or IO::SEEK_SET | Seeks to the absolute location given by _amount_ Пример:
f = File.new("testfile")
f.seek(-13, IO::SEEK_END) #=> 0
f.readline #=> "And so on...\n"
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 rb_funcallv(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;
} Если указан один аргумент, считанная строка из io помечается указанной кодировкой. Если кодировка представляет собой два имени кодировки, разделенные двоеточием «A:B», считанная строка преобразуется из кодировки A (внешняя кодировка) в кодировку B (внутренняя кодировка), а затем помечается B. Если указаны два аргумента, они должны быть объектами кодировки или именами кодировок, причем первый является внешней кодировкой, а второй — внутренней кодировкой. Если указаны внешняя и внутренняя кодировки, необязательный аргумент hash задает параметры преобразования.
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);
} Проверяет, начинается ли ios с BOM, а затем потребляет его и устанавливает внешнюю кодировку. Возвращает результирующую кодировку, если найдена, или nil. Если ios не находится в binmode или его кодировка уже установлена, будет выброшено исключение.
File.write("bom.txt", "\u{FEFF}abc")
ios = File.open("bom.txt", "rb")
ios.set_encoding_by_bom #=> #<Encoding:UTF-8>
File.write("nobom.txt", "abc")
ios = File.open("nobom.txt", "rb")
ios.set_encoding_by_bom #=> nil
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 (fptr->mode & FMODE_SYNC) ? Qtrue : Qfalse;
} Возвращает текущий «режим синхронизации» ios. Когда режим синхронизации включен (true), весь вывод немедленно сбрасывается в базовую операционную систему и не буферизуется внутренне в Ruby. См. также IO#fsync.
f = File.new("testfile")
f.sync #=> false
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. Когда режим синхронизации включен (true), весь вывод немедленно сбрасывается в базовую операционную систему и не буферизуется внутренне. Возвращает новое состояние. См. также IO#fsync.
f = File.new("testfile")
f.sync = true
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");
}
/*
* FIXME: removing rb_thread_wait_fd() here changes sysread semantics
* on non-blocking IOs. However, it's still currently possible
* for sysread to raise Errno::EAGAIN if another thread read()s
* the IO after we return from rb_thread_wait_fd() but before
* we call read()
*/
rb_thread_wait_fd(fptr->fd);
rb_io_check_closed(fptr);
io_setstrbuf(&str, ilen);
iis.fd = fptr->fd;
iis.nonblock = 1; /* for historical reasons, maybe (see above) */
iis.buf = RSTRING_PTR(str);
iis.capa = ilen;
n = read_internal_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;
} Считывает maxlen байтов из ios с помощью низкоуровневого чтения и возвращает их в виде строки. Не следует смешивать с другими методами, которые считывают из ios, иначе могут возникнуть непредсказуемые результаты.
Если присутствует необязательный аргумент outbuf, он должен ссылаться на String, который получит данные. outbuf будет содержать только полученные данные после вызова метода, даже если он не пуст в начале.
Вызывает исключение SystemCallError при ошибке и EOFError в конце файла.
f = File.new("testfile")
f.sysread(16) #=> "This is line one"
static VALUE
rb_io_sysseek(int argc, VALUE *argv, VALUE io)
{
VALUE offset, ptrname;
int whence = SEEK_SET;
rb_io_t *fptr;
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);
} Перемещает указатель на заданный offset в потоке в соответствии со значением whence (см. IO#seek для значений whence). Возвращает новый смещение в файле.
f = File.new("testfile")
f.sysseek(-13, IO::SEEK_END) #=> 53
f.sysread(10) #=> "And so on."
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_write_internal(fptr->fd, ptr, len);
if (n < 0) rb_sys_fail_path(fptr->pathv);
rb_str_tmp_frozen_release(str, tmp);
return LONG2FIX(n);
} Записывает заданную строку в ios используя низкоуровневую запись. Возвращает количество записанных байтов. Не следует смешивать с другими методами, которые записывают в ios, иначе могут возникнуть непредсказуемые результаты. Вызывает SystemCallError при ошибке.
f = File.new("out", "w")
f.syswrite("ABCDEF") #=> 6
static VALUE
rb_io_tell(VALUE io)
{
rb_io_t *fptr;
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);
} Возвращает текущее смещение (в байтах) ios.
f = File.new("testfile")
f.pos #=> 0
f.gets #=> "This is line one\n"
f.pos #=> 17
Возвращает целое число, представляющее числовой дескриптор файла для ios.
$stdin.fileno #=> 0 $stdout.fileno #=> 1
static VALUE
rb_io_to_io(VALUE io)
{
return io;
} Возвращает ios.
Возвращает true если ios связан с терминальным устройством (tty), false в противном случае.
File.new("testfile").isatty #=> false
File.new("/dev/tty").isatty #=> true
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;
} Возвращает байты (переданные в качестве параметра) в ios, так что последующее буферизованное чтение вернет их. Только один байт может быть возвращен перед последующей операцией чтения (то есть вы сможете прочитать только последний из нескольких байтов, которые были возвращены). Не имеет эффекта с небуферизованным чтением (таким как IO#sysread).
f = File.new("testfile") #=> #<File:testfile>
b = f.getbyte #=> 0x38
f.ungetbyte(b) #=> nil
f.getbyte #=> 0x38
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_TYPE_P(c, T_BIGNUM)) {
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;
} Возвращает один символ (переданный в качестве параметра) в ios, так что последующее буферизованное чтение символов вернет его. Только один символ может быть возвращен перед последующей операцией чтения (то есть вы сможете прочитать только последний из нескольких символов, которые были возвращены). Не имеет эффекта с небуферизованным чтением (таким как IO#sysread).
f = File.new("testfile") #=> #<File:testfile>
c = f.getc #=> "8"
f.ungetc(c) #=> nil
f.getc #=> "8"
static VALUE
io_wait(int argc, VALUE *argv, VALUE io)
{
VALUE timeout = Qnil;
rb_io_event_t events = 0;
if (argc < 2 || (argc >= 2 && RB_SYMBOL_P(argv[1]))) {
if (argc > 0) {
timeout = argv[0];
}
for (int i = 1; i < argc; i += 1) {
events |= wait_mode_sym(argv[i]);
}
}
else if (argc == 2) {
events = RB_NUM2UINT(argv[0]);
if (argv[1] != Qnil) {
timeout = argv[1];
}
}
else {
// TODO error
return Qnil;
}
if (events == 0) {
events = RUBY_IO_READABLE;
}
if (events & RUBY_IO_READABLE) {
rb_io_t *fptr = NULL;
RB_IO_POINTER(io, fptr);
if (rb_io_read_pending(fptr)) {
return Qtrue;
}
}
return io_wait_event(io, events, timeout);
} Ожидает, пока IO не станет готов для указанных событий и возвращает подмножество событий, которые становятся готовыми, или false при истечении времени ожидания.
События могут быть битовой маской IO::READABLE, IO::WRITABLE или IO::PRIORITY.
Возвращает true немедленно, когда доступны буферизованные данные.
Необязательный параметр mode является одним из :read, :write или :read_write (устарело).
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);
} Ожидает, пока IO не станет приоритетным и возвращает true или false при истечении времени ожидания.
static VALUE
io_wait_readable(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_READABLE, timeout);
} Ожидает, пока IO не станет доступен для чтения и возвращает true, или false при истечении времени ожидания. Возвращает true немедленно, когда доступны буферизованные данные.
static VALUE
io_wait_writable(int argc, VALUE *argv, VALUE io)
{
rb_io_t *fptr = NULL;
RB_IO_POINTER(io, fptr);
rb_io_check_writable(fptr);
rb_check_arity(argc, 0, 1);
VALUE timeout = (argc == 1 ? argv[0] : Qnil);
return io_wait_event(io, RUBY_IO_WRITABLE, timeout);
} Ожидает, пока IO не станет доступен для записи и возвращает true или false при истечении времени ожидания.
static VALUE
console_winsize(VALUE io)
{
rb_io_t *fptr;
int fd;
rb_console_size_t ws;
GetOpenFile(io, fptr);
fd = GetWriteFD(fptr);
if (!getwinsize(fd, &ws)) sys_fail_fptr(fptr);
return rb_assoc_new(INT2NUM(winsize_row(&ws)), INT2NUM(winsize_col(&ws)));
} Возвращает размер консоли.
Необходимо require 'io/console' для использования этого метода.
static VALUE
console_set_winsize(VALUE io, VALUE size)
{
rb_io_t *fptr;
rb_console_size_t ws;
#if defined _WIN32
HANDLE wh;
int newrow, newcol;
BOOL ret;
#endif
VALUE row, col, xpixel, ypixel;
const VALUE *sz;
int fd;
long sizelen;
GetOpenFile(io, fptr);
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(fptr);
#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_fptr(fptr);
#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;
} Пытается установить размер консоли. Эффект зависит от платформы и среды выполнения.
Необходимо require '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);
}
} Записывает заданные строки в ios. Поток должен быть открыт для записи. Аргументы, которые не являются строкой, будут преобразованы в строку с помощью to_s. Возвращает общее количество записанных байтов.
count = $stdout.write("This is", " a test\n")
puts "That was #{count} bytes of data"
выводит:
This is a test That was 15 bytes of data
# 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–2020 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.