класс IO
Библиотека Expect добавляет метод экземпляра IO expect, который выполняет действия, аналогичные расширению tcl's expect.
Для использования этого метода необходимо подключить expect:
require 'expect'
Подробности использования см. в expect.
Класс IO является основой для всех операций ввода-вывода в Ruby. Поток ввода-вывода может быть дуплексным (то есть двунаправленным) и может использовать более одного потока операционной системы.
Многие примеры в этом разделе используют класс File, единственный стандартный подкласс IO. Два класса тесно связаны. Как и класс File, библиотека Socket использует подклассы IO (такие как TCPSocket или UDPSocket).
Метод Kernel#open может создать объект IO (или File) для таких аргументов:
-
Простая строка представляет имя файла, подходящее для операционной системы.
-
Строка, начинающаяся с
"|"указывает на дочерний процесс. Остальная часть строки после"|"вызывается как процесс с соответствующими каналами ввода/вывода, подключенными к нему. -
Строка, равная
"|-"создаст другой экземпляр Ruby как дочерний процесс.
Объект IO может быть открыт с различными режимами файла (только чтение, только запись) и кодировками для правильного преобразования. См. ::new для этих вариантов. Подробности различных форматов команд, описанных выше, см. в Kernel#open.
::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 предоставляет методы для взаимодействия с консолью. Консоль доступна из ::console или стандартных объектов ввода/вывода/ошибки IO.
Подключение io/console добавляет следующие методы:
Пример:
require 'io/console'
rows, columns = $stdin.winsize
puts "Your screen is #{columns} wide and #{rows} tall"
Константы
- EWOULDBLOCKWaitReadable
-
EAGAINWaitReadable
- EWOULDBLOCKWaitWritable
-
EAGAINWaitWritable
- SEEK_CUR
-
Установить позицию ввода-вывода относительно текущей позиции
- SEEK_DATA
-
Установить позицию ввода-вывода в следующую позицию, содержащую данные
- SEEK_END
-
Установить позицию ввода-вывода относительно конца
- SEEK_HOLE
-
Установить позицию ввода-вывода в следующую позицию-дыру
- SEEK_SET
-
Установить позицию ввода-вывода относительно начала
Методы открытого класса
static VALUE
rb_io_s_binread(int argc, VALUE *argv, VALUE io)
{
VALUE offset;
struct foreach_arg arg;
rb_scan_args(argc, argv, "12", NULL, NULL, &offset);
FilePathValue(argv[0]);
arg.io = rb_io_open(argv[0], rb_str_new_cstr("rb:ASCII-8BIT"), Qnil, Qnil);
if (NIL_P(arg.io)) return Qnil;
arg.argv = argv+1;
arg.argc = (argc > 1) ? 1 : 0;
if (!NIL_P(offset)) {
rb_io_seek(arg.io, offset, SEEK_SET);
}
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, 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, 1);
if (argc) {
Check_Type(sym = argv[0], T_SYMBOL);
--argc;
++argv;
}
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) {
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) {
/* TODO: avoid inadvertent pindown */
return rb_funcall(con, SYM2ID(sym), argc, argv);
}
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_fd_init(&st.fds);
rb_ensure(copy_stream_body, (VALUE)&st, copy_stream_finalize, (VALUE)&st);
return OFFT2NUM(st.total);
} ::copy_stream копирует содержимое src в dst. src и dst может быть именем файла или объектом IO.
Метод возвращает количество скопированных байтов.
Если необязательные аргументы не заданы, начальная позиция копирования — начало файла или текущая позиция в файле объекта IO. Конечная позиция копирования — конец файла.
Если задан copy_length, то не более copy_length байтов будут скопированы.
Если задан src_offset, то он определяет начальную позицию копирования.
Если задан src_offset и src является объектом IO, ::copy_stream не перемещает текущую позицию в файле.
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;
argc = rb_scan_args(argc, argv, "13:", NULL, NULL, NULL, NULL, &opt);
RETURN_ENUMERATOR(self, orig_argc, argv);
open_key_args(argc, argv, opt, &arg);
if (NIL_P(arg.io)) return Qnil;
return rb_ensure(io_s_foreach, (VALUE)&arg, rb_io_close, arg.io);
} Выполняет блок для каждой строки в указанном I/O порту, где строки разделены символом 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.read для деталей.
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) == -1) 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->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. См. также ::sysopen и ::for_fd.
::new вызывается различными методами открытия файлов и потоков File и 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 Если режим открытия исходного IO — только чтение, его нельзя изменить на запись. Аналогично, режим открытия нельзя изменить с только записи на чтение.
При попытке такого изменения ошибка генерируется в разных местах в зависимости от платформы.
IO Кодировка
Если указана ext_enc, при чтении строки будут помечены кодировкой, а при записи строки будут преобразованы в указанную кодировку.
Если указаны ext_enc и int_enc, введённые строки будут преобразованы из ext_enc в int_enc при вводе, а выводимые строки будут преобразованы из int_enc в ext_enc при выводе. См. Кодировку для получения более подробной информации о преобразовании кодировок при вводе и выводе.
Если используются “BOM|UTF-8”, “BOM|UTF-16LE” или “BOM|UTF16-BE”, Ruby проверяет BOM Unicode в входном документе для определения кодировки. Для кодировок UTF-16 режим открытия файла должен быть двоичным. При наличии BOM он удаляется, и внешняя кодировка из BOM используется. Если BOM отсутствует, заданная кодировка Unicode используется как ext_enc. (Вариант кодировки с BOM нечувствителен к регистру, поэтому “bom|utf-8” также допустим.)
Параметры
opt может быть использовано вместо mode для лучшей читаемости. Поддерживаются следующие ключи:
- :mode
-
То же, что и параметр
mode - :external_encoding
-
Внешняя кодировка для IO. “-” — синоним для стандартной внешней кодировки.
- :internal_encoding
-
Внутренняя кодировка для IO. “-” — синоним для стандартной внутренней кодировки.
Если значение
nil, преобразование не происходит. - :encoding
-
Указывает внешнюю и внутреннюю кодировки как “extern:intern”.
- :textmode
-
Если значение истинно, то аналогично “t” в аргументе
mode. - :binmode
-
Если значение истинно, то аналогично “b” в аргументе
mode. - :autoclose
-
Если значение
false, то IO будет оставаться открытым после завершения работы экземпляра 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!"
Оба примера выводят “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(argc, argv, klass);
if (rb_block_given_p()) {
return rb_ensure(rb_yield, io, io_close, io);
}
return io;
} Без связанного блока, IO.open является синонимом для ::new. Если задан необязательный блок кода, он будет передан io в качестве аргумента, и объект IO будет автоматически закрыт по завершении блока. В этом случае ::open возвращает значение блока.
См. ::new для описания параметров fd, mode и opt.
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) == -1)
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 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)
{
const char *modestr;
VALUE pname, pmode = Qnil, port, tmp, opt = Qnil, env = Qnil, execarg_obj = Qnil;
int oflags, fmode;
convconfig_t convconfig;
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);
}
}
tmp = rb_check_array_type(pname);
if (!NIL_P(tmp)) {
long len = RARRAY_LEN(tmp);
#if SIZEOF_LONG > SIZEOF_INT
if (len > INT_MAX) {
rb_raise(rb_eArgError, "too many arguments");
}
#endif
execarg_obj = rb_execarg_new((int)len, RARRAY_CONST_PTR(tmp), FALSE);
RB_GC_GUARD(tmp);
}
else {
SafeStringValue(pname);
execarg_obj = Qnil;
if (!is_popen_fork(pname))
execarg_obj = rb_execarg_new(1, &pname, TRUE);
}
if (!NIL_P(execarg_obj)) {
if (!NIL_P(opt))
opt = rb_execarg_extract_options(execarg_obj, opt);
if (!NIL_P(env))
rb_execarg_setenv(execarg_obj, env);
}
rb_io_extract_modeenc(&pmode, 0, opt, &oflags, &fmode, &convconfig);
modestr = rb_io_oflags_modestr(oflags);
port = pipe_open(execarg_obj, modestr, fmode, &convconfig);
if (NIL_P(port)) {
/* child */
if (rb_block_given_p()) {
rb_yield(Qnil);
rb_io_flush(rb_stdout);
rb_io_flush(rb_stderr);
_exit(0);
}
return Qnil;
}
RBASIC_SET_CLASS(port, klass);
if (rb_block_given_p()) {
return rb_ensure(rb_yield, port, io_close, port);
}
return port;
} Запускает указанную команду как дочерний процесс; стандартный ввод и вывод дочернего процесса будут подключены к возвращённому IO объекту.
Идентификатор процесса (PID) запущенного процесса можно получить с помощью метода #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 является строкой “-”, то запускается новый экземпляр 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(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 гарантирует, что файл закрыт до возвращения результата.
Опции
Хэш опций принимает следующие ключи:
- encoding
-
строка или кодировка
Указывает кодировку считанной строки.
encoding:будет проигнорирован, еслиlengthуказан. См. Encoding.aliases для возможных кодировок. - mode
-
строка
Указывает аргумент режима для open(). Он должен начинаться с «r», иначе произойдёт ошибка. См. ::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;
argc = rb_scan_args(argc, argv, "13:", NULL, NULL, NULL, NULL, &opt);
open_key_args(argc, argv, opt, &arg);
if (NIL_P(arg.io)) return Qnil;
return rb_ensure(io_s_readlines, (VALUE)&arg, rb_io_close, arg.io);
} Читает весь файл, указанный name, как отдельные строки и возвращает эти строки в массиве. Строки разделены sep.
a = IO.readlines("testfile")
a[0] #=> "This is line one\n"
Если последний аргумент — хэш, это ключевой аргумент для open. См. IO.read для деталей.
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, когда несколько процессов читают из одного потока.
Наконец, разработчики ядра 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 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);
} Открывает заданный путь, возвращая базовый дескриптор файла как Fixnum.
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, 0);
} Открывает файл, необязательно перемещает указатель на заданный offset, записывает string, затем возвращает длину записанных данных. write гарантирует закрытие файла перед возвращением. Если offset не задан, файл обрезается. В противном случае, он не обрезается.
Если последний аргумент является хэш-таблицей, он определяет опции для внутреннего открытия(). Ключ будет следующим. open_args: является уникальным для остальных.
encoding: string or encoding
specifies encoding of the read string. encoding will be ignored
if length is specified.
mode: string
specifies mode argument for open(). it should start with "w" or "a" or "r+"
otherwise it would cause error.
perm: fixnum
specifies perm argument for open().
open_args: array
specifies arguments for open() as an array.
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" Открытые методы экземпляра
VALUE
rb_io_addstr(VALUE io, VALUE str)
{
rb_io_write(io, str);
return io;
} Строковый вывод—Записывает 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.
«Данные» означают область текущего файла, которая начинается со смещения и простирается на 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 io;
} Устанавливает флаг автоматического закрытия.
f = open("/dev/null")
IO.for_fd(f.fileno)
# ...
f.gets # may cause IOError
f = open("/dev/null")
IO.for_fd(f.fileno).autoclose = true
# ...
f.gets # won't cause IOError
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
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
rb_io_bytes(VALUE io)
{
rb_warn("IO#bytes is deprecated; use #each_byte instead");
if (!rb_block_given_p())
return rb_enumeratorize(io, ID2SYM(rb_intern("each_byte")), 0, 0);
return rb_io_each_byte(io);
} Это устаревшее псевдоним для each_byte.
static VALUE
rb_io_chars(VALUE io)
{
rb_warn("IO#chars is deprecated; use #each_char instead");
if (!rb_block_given_p())
return rb_enumeratorize(io, ID2SYM(rb_intern("each_char")), 0, 0);
return rb_io_each_char(io);
} Это устаревшее псевдоним для each_char.
static VALUE
rb_io_close_m(VALUE io)
{
rb_io_check_closed(RFILE(io)->fptr);
rb_io_close(io);
return Qnil;
} Закрывает ios и очищает все ожидающие записи в операционную систему. Поток становится недоступен для дальнейших операций с данными; если будет предпринята такая попытка, будет возбуждено исключение IOError. Потоки ввода-вывода автоматически закрываются, когда они запрашиваются сборщиком мусора.
Если ios открыт с помощью IO.popen, close устанавливает $?.
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 == -1) 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 == -1) 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;
GetOpenFile(io, fptr);
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;
GetOpenFile(write_io, wfptr);
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;
fptr->mode &= ~FMODE_DUPLEX;
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_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
static VALUE
rb_io_close_write(VALUE io)
{
rb_io_t *fptr;
VALUE write_io;
write_io = GetWriteIO(io);
GetOpenFile(write_io, fptr);
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_READABLE) {
rb_raise(rb_eIOError, "closing non-duplex IO for writing");
}
if (io != write_io) {
GetOpenFile(io, fptr);
fptr->tied_io_for_writing = 0;
fptr->mode &= ~FMODE_DUPLEX;
}
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
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 = RFILE(io)->fptr;
rb_io_check_initialized(fptr);
return 0 <= fptr->fd ? Qfalse : Qtrue;
} Возвращает true, если ios полностью закрыт (для дуплексных потоков — и читатель, и писатель), 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
rb_io_codepoints(VALUE io)
{
rb_warn("IO#codepoints is deprecated; use #each_codepoint instead");
if (!rb_block_given_p())
return rb_enumeratorize(io, ID2SYM(rb_intern("each_codepoint")), 0, 0);
return rb_io_each_codepoint(io);
} Это устаревшее псевдоним для each_codepoint.
static VALUE
console_cooked(VALUE io)
{
return ttymode(io, rb_yield, 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)) rb_sys_fail(0);
set_cookedmode(&t, NULL);
if (!setattr(fd, &t)) rb_sys_fail(0);
return io;
} Включает cooked режим.
Если необходимо вернуть режим терминала, используйте io.cooked { … }.
Для использования этого метода необходимо подключить 'io/console'.
static VALUE
rb_io_each_line(int argc, VALUE *argv, VALUE io)
{
VALUE str, rs;
long limit;
RETURN_ENUMERATOR(io, argc, argv);
prepare_getline_args(argc, argv, &rs, &limit, io);
if (limit == 0)
rb_raise(rb_eArgError, "invalid limit: 0 for each_line");
while (!NIL_P(str = rb_io_getline_1(rs, limit, 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...
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)) {
invalid:
rb_raise(rb_eArgError, "invalid byte sequence in %s", rb_enc_name(enc));
}
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;
} Передает порядковый номер каждого символа в ios, передавая код символа в качестве аргумента. Поток должен быть открыт для чтения, иначе будет поднято IOError.
Если блок не указан, вместо него возвращается перечислитель.
static VALUE
rb_io_each_line(int argc, VALUE *argv, VALUE io)
{
VALUE str, rs;
long limit;
RETURN_ENUMERATOR(io, argc, argv);
prepare_getline_args(argc, argv, &rs, &limit, io);
if (limit == 0)
rb_raise(rb_eArgError, "invalid limit: 0 for each_line");
while (!NIL_P(str = rb_io_getline_1(rs, limit, 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...
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)) rb_sys_fail(0);
if (RTEST(f))
set_echo(&t, NULL);
else
set_noecho(&t, NULL);
if (!setattr(fd, &t)) rb_sys_fail(0);
return io;
} Включает/выключает отображение ввода. На некоторых платформах все комбинации этих флагов и режимов raw/cooked могут быть недействительными.
Для использования этого метода необходимо require '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)) rb_sys_fail(0);
return echo_p(&t) ? Qtrue : Qfalse;
} Возвращает true, если отображение ввода включено.
Для использования этого метода необходимо require '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 предварительно (что недоступно для некоторых потоков).
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 предварительно (что недоступно для некоторых потоков).
# File ext/pty/lib/expect.rb, line 32
def expect(pat,timeout=9999999)
buf = ''
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).chr
elsif !IO.select([self],nil,nil,timeout) or eof? then
result = nil
@unusedBuf = buf
break
else
c = getc.chr
end
buf << c
if $expect_verbose
STDOUT.print c
STDOUT.flush
end
if mat=e_pat.match(buf) then
result = [buf,*mat.to_a[1..-1]]
break
end
end
if block_given? then
yield result
else
return result
end
nil
end Считывает из IO до тех пор, пока заданный pattern не совпадёт или timeout не закончится.
Возвращает массив с прочитанным буфером, за которым следуют совпадения. Если задан блок, результат передаётся в блок, и возвращается null.
При вызове без блока ожидает, пока ввод, соответствующий заданному pattern, не будет получен из IO, или не истечёт заданное время ожидания. Массив возвращается, когда шаблон получен из IO. Первый элемент массива — вся строка, полученная из IO до совпадения шаблона, за которым следуют элементы, указывающие, какой шаблон сопоставился с якорем в регулярном выражении.
Необязательный параметр timeout определяет общее время ожидания шаблона в секундах. Если таймаут истекает или обнаруживается eof, возвращается или передаётся null. Однако буфер в сессии таймаута сохраняется для следующего вызова expect. Значение по умолчанию для таймаута — 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(0);
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(0);
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, но не гарантирует, что базовая операционная система фактически запишет их на диск.
NotImplementedError генерируется, если базовая операционная система не поддерживает fsync(2).
VALUE
rb_io_getbyte(VALUE io)
{
rb_io_t *fptr;
int c;
GetOpenFile(io, fptr);
rb_io_check_byte_readable(fptr);
READ_CHECK(fptr);
if (fptr->fd == 0 && (fptr->mode & FMODE_TTY) && RB_TYPE_P(rb_stdout, T_FILE)) {
rb_io_t *ofp;
GetOpenFile(rb_stdout, ofp);
if (ofp->mode & FMODE_TTY) {
rb_io_flush(rb_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, &opts);
return ttymode(io, getc_call, set_rawmode, optp);
} Считывает и возвращает символ в сыром режиме.
Для использования этого метода необходимо подключить '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"
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)) rb_sys_fail(0);
#endif
(void)fd;
return io;
} Очищает буфер ввода в ядре.
Для использования этого метода необходимо подключить 'io/console'.
static VALUE
rb_io_inspect(VALUE obj)
{
rb_io_t *fptr;
VALUE result;
static const char closed[] = " (closed)";
fptr = RFILE(obj)->fptr;
if (!fptr) return rb_any_to_s(obj);
result = rb_str_new_cstr("#<");
rb_str_append(result, rb_class_name(CLASS_OF(obj)));
rb_str_cat2(result, ":");
if (NIL_P(fptr->pathv)) {
if (fptr->fd < 0) {
rb_str_cat(result, closed+1, strlen(closed)-1);
}
else {
rb_str_catf(result, "fd %d", fptr->fd);
}
}
else {
rb_str_append(result, fptr->pathv);
if (fptr->fd < 0) {
rb_str_cat(result, closed, strlen(closed));
}
}
return rb_str_cat2(result, ">");
} Возвращает строку, описывающую объект 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));
} Возвращает Кодировку внутренней строки, если задано преобразование. В противном случае возвращает 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)) rb_sys_fail(0);
if (tcflush(fd2, TCOFLUSH)) rb_sys_fail(0);
}
else {
if (tcflush(fd1, TCIOFLUSH)) rb_sys_fail(0);
}
#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
rb_io_lines(int argc, VALUE *argv, VALUE io)
{
rb_warn("IO#lines is deprecated; use #each_line instead");
if (!rb_block_given_p())
return rb_enumeratorize(io, ID2SYM(rb_intern("each_line")), argc, argv);
return rb_io_each_line(argc, argv, io);
} Это устаревший алиас для each_line.
static VALUE
console_noecho(VALUE io)
{
return ttymode(io, rb_yield, 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;
io_nonblock_set(fptr->fd, f, nb);
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;
int len;
ioctl_arg n;
GetOpenFile(io, fptr);
rb_io_check_readable(fptr);
len = rb_io_read_pending(fptr);
if (len > 0) return INT2FIX(len);
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)) rb_sys_fail(0);
#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
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
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"
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;
}
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 в ios. Примечание: этот метод небезопасен для использования с многобайтовыми символами, так как он их обрезает.
$stdout.putc "A" $stdout.putc 65
возвращает:
AA
VALUE
rb_io_puts(int argc, const VALUE *argv, VALUE out)
{
int i;
VALUE line;
/* 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:
rb_io_write(out, line);
if (RSTRING_LEN(line) == 0 ||
!str_end_with_asciichar(line, '\n')) {
rb_io_write(out, rb_default_rs);
}
}
return Qnil;
} Записывает заданный объект(ы) в ios, как и в IO#print. Добавляет символ новой строки после объектов, которые им не заканчиваются.
Если в качестве аргумента передаётся массив, каждый элемент записывается с новой строки. Если метод вызывается без аргументов, выводится одиночная новая строка.
$stdout.puts("this", "is", "a", "test")
возвращает:
this is a test
static VALUE
console_raw(int argc, VALUE *argv, VALUE io)
{
rawmode_arg_t opts, *optp = rawmode_opt(argc, argv, &opts);
return ttymode(io, rb_yield, set_rawmode, optp);
} Возвращает self в режиме raw.
STDIN.raw(&:gets)
будет читать и возвращать строку без эха и редактирования.
Для использования этого метода необходимо подключить модуль '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, &opts);
GetOpenFile(io, fptr);
fd = GetReadFD(fptr);
if (!getattr(fd, &t)) rb_sys_fail(0);
set_rawmode(&t, optp);
if (!setattr(fd, &t)) rb_sys_fail(0);
return 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;
#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);
}
io_setstrbuf(&str,len);
GetOpenFile(io, fptr);
rb_io_check_byte_readable(fptr);
if (len == 0) {
io_set_read_length(str, 0);
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);
#if defined(RUBY_TEST_CRLF_ENVIRONMENT) || defined(_WIN32)
if (previous_mode == O_TEXT) {
setmode(fptr->fd, O_TEXT);
}
#endif
if (n == 0) return Qnil;
OBJ_TAINT(str);
return str;
} Считывает length байтов из потока ввода/вывода.
length должен быть неотрицательным целым числом или nil.
Если length положительное целое число, read пытается прочитать length байт без преобразований (бинарный режим). Возвращает nil если EOF был встречен до считывания каких-либо данных. Если при чтении встретился EOF до чтения всех байтов, то возвращается количество прочитанных байтов. В случае целого значения length, результирующая строка всегда будет в кодировке ASCII-8BIT.
Если length опущен или равен nil, считывается до EOF и применяется преобразование кодировки (если применимо). Возвращается строка даже если EOF встречен до считывания данных.
Если length равен нулю, возвращается пустая строка ("").
Если присутствует необязательный аргумент outbuf, он должен ссылаться на строку, в которую будут записаны данные. 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.
static VALUE
io_read_nonblock(int argc, VALUE *argv, VALUE io)
{
VALUE ret;
VALUE opts = Qnil;
int no_exception = 0;
rb_scan_args(argc, argv, "11:", NULL, NULL, &opts);
if (!NIL_P(opts) && Qfalse == get_kwargs_exception(opts)) {
no_exception = 1;
argc--;
}
ret = io_getpartial(argc, argv, io, 1, no_exception);
if (NIL_P(ret)) {
if (no_exception)
return Qnil;
else
rb_eof_error();
}
return ret;
} Считывает не более maxlen байтов из ios, используя системный вызов read(2) после установки O_NONBLOCK для базового дескриптора файла.
Если необязательный аргумент outbuf присутствует, он должен ссылаться на строку, в которую будут записаны данные. outbuf будет содержать только полученные данные после вызова метода, даже если он не был пустым изначально.
#read_nonblock просто вызывает системный вызов read(2). Он вызывает все ошибки, которые может вызвать read(2): Errno::EWOULDBLOCK, Errno::EINTR и т. д. Вызывающий код должен обрабатывать такие ошибки.
Если возникает исключение Errno::EWOULDBLOCK или Errno::AGAIN, оно обрабатывается классом IO::WaitReadable. Таким образом, IO::WaitReadable может использоваться для перехвата исключений для повторной попытки read_nonblock.
#read_nonblock вызывает EOFError при достижении EOF.
Если буфер считанных байтов не пуст, #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
Хотя #read_nonblock не вызывает IO::WaitWritable. OpenSSL::Buffering#read_nonblock может вызвать IO::WaitWritable. Если IO и SSL должны использоваться полиморфно, IO::WaitWritable также должен быть перехвачен. См. документацию OpenSSL::Buffering#read_nonblock для примера кода.
Обратите внимание, что этот метод идентичен readpartial, за исключением установки флага без блокировки.
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_readline(int argc, VALUE *argv, VALUE io)
{
VALUE line = rb_io_gets_m(argc, argv, io);
if (NIL_P(line)) {
rb_eof_error();
}
return line;
} Считывает строку, как и IO#gets, но при достижении конца файла генерирует исключение EOFError.
static VALUE
rb_io_readlines(int argc, VALUE *argv, VALUE io)
{
VALUE line, ary, rs;
long limit;
prepare_getline_args(argc, argv, &rs, &limit, io);
if (limit == 0)
rb_raise(rb_eArgError, "invalid limit: 0 for readlines");
ary = rb_ary_new();
while (!NIL_P(line = rb_io_getline_1(rs, limit, io))) {
rb_ary_push(ary, line);
}
return ary;
} Считывает все строки из ios и возвращает их в anArray. Строки разделяются необязательным sep. Если sep — nil, вся оставшаяся часть потока возвращается как один элемент. Если первый аргумент — целое число или задан необязательный второй аргумент, возвращаемая строка не будет длиннее заданного значения в байтах. Поток должен быть открыт для чтения, иначе будет сгенерировано исключение IOError.
f = File.new("testfile")
f.readlines[0] #=> "This is line one\n"
static VALUE
io_readpartial(int argc, VALUE *argv, VALUE io)
{
VALUE ret;
ret = io_getpartial(argc, argv, io, 0, 0);
if (NIL_P(ret))
rb_eof_error();
return ret;
} Считывает не более maxlen байтов из потока ввода-вывода. Блокируется только если в ios нет данных, немедленно доступных. Не блокируется, если некоторые данные доступны.
Если задан необязательный аргумент outbuf, он должен ссылаться на строку, которая получит данные. 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 от системного вызова read, readpartial повторно пытается выполнить системный вызов.
Последнее означает, что readpartial не чувствителен к флагам без блокировки. Он блокируется в ситуации, когда #sysread вызывает Errno::EWOULDBLOCK, как будто fd находится в режиме блокировки.
static VALUE
io_ready_p(VALUE io)
{
rb_io_t *fptr;
ioctl_arg n;
GetOpenFile(io, fptr);
rb_io_check_readable(fptr);
if (rb_io_read_pending(fptr)) return Qtrue;
if (!FIONREAD_POSSIBLE_P(fptr->fd)) return Qnil;
if (ioctl(fptr->fd, FIONREAD, &n)) return Qnil;
if (n > 0) return Qtrue;
return Qfalse;
} Возвращает true, если данные доступны без блокировки, или false. Возвращает nil, если информация недоступна.
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 = rb_str_new_frozen(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(0);
}
fptr->rbuf.off = fptr->rbuf.len = 0;
if (fptr->stdio_file) {
if (freopen(RSTRING_PTR(fptr->pathv), rb_io_oflags_modestr(oflags), fptr->stdio_file) == 0) {
rb_sys_fail_path(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. Это может динамически изменить фактический класс этого потока.
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"
Обратите внимание, что его нельзя использовать с потоками, такими как трубы, tty и сокеты.
# File lib/scanf.rb, line 614
def scanf(str,&b) #:yield: current_match
return block_scanf(str,&b) if b
return [] unless str.size > 0
start_position = pos rescue 0
matched_so_far = 0
source_buffer = ""
result_buffer = []
final_result = []
fstr = Scanf::FormatString.new(str)
loop do
if eof || (tty? &&! fstr.match(source_buffer))
final_result.concat(result_buffer)
break
end
source_buffer << gets
current_match = fstr.match(source_buffer)
spec = fstr.last_spec_tried
if spec.matched
if spec.mid_match?
result_buffer.replace(current_match)
next
end
elsif (fstr.matched_count == fstr.spec_count - 1)
if /\A\s*\z/.match(fstr.string_left)
break if spec.count_space?
result_buffer.replace(current_match)
next
end
end
final_result.concat(current_match)
matched_so_far += source_buffer.size
source_buffer.replace(fstr.string_left)
matched_so_far -= source_buffer.size
break if fstr.last_spec
fstr.prune
end
begin
seek(start_position + matched_so_far, IO::SEEK_SET)
rescue Errno::ESPIPE
end
soak_up_spaces if fstr.last_spec && fstr.space
return final_result
end Сканирует текущую строку до тех пор, пока сопоставление не иссякнет, выдавая каждое сопоставление по мере его обнаружения в строке. Блок не является обязательным, так как результаты просто агрегируются в итоговый массив.
"123 456".block_scanf("%d")
# => [123, 456]
Если задан блок, значение, возвращаемое из yield, добавляется в выходной массив.
"123 456".block_scanf("%d") do |digit,| # the ',' unpacks the Array
digit + 100
end
# => [223, 556]
Подробности по созданию строки формата см. в Scanf.
Для использования #scanf необходимо выполнить require 'scanf'.
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_funcall2(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», строка, считанная из io, преобразуется из кодирования A (внешнее кодирование) в кодирование B (внутреннее кодирование), а затем помечена кодированием B. Если заданы два аргумента, они должны быть объектами кодирования или именами кодирования, и первый — внешнее кодирование, а второй — внутреннее кодирование. Если внешнее и внутреннее кодирование заданы, необязательный аргумент в виде хэша задает параметры преобразования.
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. Когда режим синхронизации включен, весь вывод немедленно записывается в операционную систему и не буферизуется 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. Когда режим синхронизации включен, весь вывод немедленно записывается в операционную систему и не буферизуется. Возвращает новое состояние. См. также 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 read_internal_arg arg;
rb_scan_args(argc, argv, "11", &len, &str);
ilen = NUM2LONG(len);
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);
rb_str_locktmp(str);
arg.fd = fptr->fd;
arg.str_ptr = RSTRING_PTR(str);
arg.len = ilen;
rb_ensure(read_internal_call, (VALUE)&arg, rb_str_unlocktmp, str);
n = arg.len;
if (n == -1) {
rb_sys_fail_path(fptr->pathv);
}
io_set_read_length(str, n);
if (n == 0 && ilen > 0) {
rb_eof_error();
}
OBJ_TAINT(str);
return str;
} Читает maxlen байт из ios с помощью низкоуровневого чтения и возвращает их как строку. Не смешивайте с другими методами, которые читают из ios, иначе результаты могут быть непредсказуемыми.
Если присутствует необязательный аргумент outbuf, он должен ссылаться на строку, которая получит данные. 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 == -1 && 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)
{
rb_io_t *fptr;
long n;
if (!RB_TYPE_P(str, T_STRING))
str = rb_obj_as_string(str);
io = GetWriteIO(io);
GetOpenFile(io, fptr);
rb_io_check_writable(fptr);
str = rb_str_new_frozen(str);
if (fptr->wbuf.len) {
rb_warn("syswrite for buffered IO");
}
n = rb_write_internal(fptr->fd, RSTRING_PTR(str), RSTRING_LEN(str));
RB_GC_GUARD(str);
if (n == -1) rb_sys_fail_path(fptr->pathv);
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
static VALUE
rb_io_to_io(VALUE io)
{
return io;
} Возвращает ios.
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
VALUE
rb_io_ungetbyte(VALUE io, VALUE b)
{
rb_io_t *fptr;
GetOpenFile(io, fptr);
rb_io_check_byte_readable(fptr);
if (NIL_P(b)) return Qnil;
if (FIXNUM_P(b)) {
char cc = FIX2INT(b);
b = rb_str_new(&cc, 1);
}
else {
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 (NIL_P(c)) return Qnil;
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_readable(int argc, VALUE *argv, VALUE io)
{
rb_io_t *fptr;
int i;
ioctl_arg n;
VALUE timeout;
struct timeval timerec;
struct timeval *tv;
GetOpenFile(io, fptr);
rb_io_check_readable(fptr);
rb_scan_args(argc, argv, "01", &timeout);
if (NIL_P(timeout)) {
tv = NULL;
}
else {
timerec = rb_time_interval(timeout);
tv = &timerec;
}
if (rb_io_read_pending(fptr)) return Qtrue;
if (!FIONREAD_POSSIBLE_P(fptr->fd)) return Qfalse;
i = rb_wait_for_single_fd(fptr->fd, RB_WAITFD_IN, tv);
if (i < 0)
rb_sys_fail(0);
rb_io_check_closed(fptr);
if (ioctl(fptr->fd, FIONREAD, &n)) rb_sys_fail(0);
if (n > 0) return io;
return Qnil;
} Ожидает доступности входных данных или истечения времени ожидания и возвращает self или nil при достижении конца файла.
static VALUE
io_wait_readable(int argc, VALUE *argv, VALUE io)
{
rb_io_t *fptr;
int i;
ioctl_arg n;
VALUE timeout;
struct timeval timerec;
struct timeval *tv;
GetOpenFile(io, fptr);
rb_io_check_readable(fptr);
rb_scan_args(argc, argv, "01", &timeout);
if (NIL_P(timeout)) {
tv = NULL;
}
else {
timerec = rb_time_interval(timeout);
tv = &timerec;
}
if (rb_io_read_pending(fptr)) return Qtrue;
if (!FIONREAD_POSSIBLE_P(fptr->fd)) return Qfalse;
i = rb_wait_for_single_fd(fptr->fd, RB_WAITFD_IN, tv);
if (i < 0)
rb_sys_fail(0);
rb_io_check_closed(fptr);
if (ioctl(fptr->fd, FIONREAD, &n)) rb_sys_fail(0);
if (n > 0) return io;
return Qnil;
} Ожидает доступности входных данных или истечения времени ожидания и возвращает self или nil при достижении конца файла.
static VALUE
io_wait_writable(int argc, VALUE *argv, VALUE io)
{
rb_io_t *fptr;
int i;
VALUE timeout;
struct timeval timerec;
struct timeval *tv;
GetOpenFile(io, fptr);
rb_io_check_writable(fptr);
rb_scan_args(argc, argv, "01", &timeout);
if (NIL_P(timeout)) {
tv = NULL;
}
else {
timerec = rb_time_interval(timeout);
tv = &timerec;
}
i = rb_wait_for_single_fd(fptr->fd, RB_WAITFD_OUT, tv);
if (i < 0)
rb_sys_fail(0);
rb_io_check_closed(fptr);
if (i & RB_WAITFD_OUT)
return io;
return Qnil;
} Ожидает доступности для записи объекта IO или истечения времени ожидания и возвращает self или nil при достижении конца файла.
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)) rb_sys_fail(0);
return rb_assoc_new(INT2NUM(winsize_row(&ws)), INT2NUM(winsize_col(&ws)));
} Возвращает размер консоли.
Для использования этого метода необходимо подключить '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;
#endif
VALUE row, col, xpixel, ypixel;
#if defined TIOCSWINSZ
int fd;
#endif
GetOpenFile(io, fptr);
size = rb_Array(size);
rb_scan_args((int)RARRAY_LEN(size), RARRAY_PTR(size), "22",
&row, &col, &xpixel, &ypixel);
#if defined TIOCSWINSZ
fd = GetWriteFD(fptr);
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)) rb_sys_fail(0);
#elif defined _WIN32
wh = (HANDLE)rb_w32_get_osfhandle(GetReadFD(fptr));
newrow = (SHORT)NUM2UINT(row);
newcol = (SHORT)NUM2UINT(col);
if (!getwinsize(GetReadFD(fptr), &ws)) {
rb_sys_fail("GetConsoleScreenBufferInfo");
}
if ((ws.dwSize.X < newcol && (ws.dwSize.X = newcol, 1)) ||
(ws.dwSize.Y < newrow && (ws.dwSize.Y = newrow, 1))) {
if (!(SetConsoleScreenBufferSize(wh, ws.dwSize) || SET_LAST_ERROR)) {
rb_sys_fail("SetConsoleScreenBufferInfo");
}
}
ws.srWindow.Left = 0;
ws.srWindow.Top = 0;
ws.srWindow.Right = newcol;
ws.srWindow.Bottom = newrow;
if (!(SetConsoleWindowInfo(wh, FALSE, &ws.srWindow) || SET_LAST_ERROR)) {
rb_sys_fail("SetConsoleWindowInfo");
}
#endif
return io;
} Пытается установить размер консоли. Эффект зависит от платформы и среды выполнения.
Для использования этого метода необходимо подключить 'io/console'.
static VALUE
io_write_m(VALUE io, VALUE str)
{
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
static VALUE
rb_io_write_nonblock(int argc, VALUE *argv, VALUE io)
{
VALUE str;
VALUE opts = Qnil;
int no_exceptions = 0;
rb_scan_args(argc, argv, "10:", &str, &opts);
if (!NIL_P(opts) && Qfalse == get_kwargs_exception(opts))
no_exceptions = 1;
return io_write_nonblock(io, str, no_exceptions);
} Записывает заданную строку в ios, используя системный вызов write(2), после установки O_NONBLOCK для базового дескриптора файла.
Возвращает количество записанных байт.
#write_nonblock просто вызывает системный вызов write(2). Это вызывает все ошибки, которые вызывает системный вызов write(2): Errno::EWOULDBLOCK, Errno::EINTR и т. д. Результат также может быть меньше, чем string.length (частичная запись). Вызывающий должен обрабатывать такие ошибки и частичную запись.
Если исключение — Errno::EWOULDBLOCK или Errno::AGAIN, оно расширяется с помощью 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.
Приватные методы экземпляра
# File lib/scanf.rb, line 682
def block_scanf(str)
final = []
# Sub-ideal, since another FS gets created in scanf.
# But used here to determine the number of specifiers.
fstr = Scanf::FormatString.new(str)
last_spec = fstr.last_spec
begin
current = scanf(str)
break if current.empty?
final.push(yield(current))
end until eof || fstr.last_spec_tried == last_spec
return final
end # File lib/scanf.rb, line 673
def soak_up_spaces
c = getc
ungetc(c) if c
until eof ||! c || /\S/.match(c.chr)
c = getc
end
ungetc(c) if (c && /\S/.match(c.chr))
end
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.