class ARGF
ARGF 和 ARGV
ARGF 对象与全局变量 ARGV 中的数组配合工作,使 $stdin 和文件流在 Ruby 程序中可用。
-
ARGV 可以被认为是参数向量数组。
最初,它包含传递给 Ruby 程序的命令行参数和选项;程序可以随意修改该数组。
-
ARGF 可以被认为是参数文件对象。
它可以访问文件流和/或
$stdin流,具体取决于它在ARGV中找到的内容。这为命令行指定 Ruby 程序要读取的流提供了一种便捷的方式。
读取
ARGF 可以从源流读取,这些流在任何特定时间都由 ARGV 的内容决定。
最简单的情况
当第一次 ARGF 读取发生在 ARGV 为空 ([]) 的情况下时,源是 $stdin。
-
文件
t.rbp ['ARGV', ARGV] p ['ARGF.read', ARGF.read]
-
命令和输出(
foo.txt和bar.txt文件的内容请参见下文)$ echo "Open the pod bay doors, Hal." | ruby t.rb ["ARGV", []] ["ARGF.read", "Open the pod bay doors, Hal.\n"] $ cat foo.txt bar.txt | ruby t.rb ["ARGV", []] ["ARGF.read", "Foo 0\nFoo 1\nBar 0\nBar 1\nBar 2\nBar 3\n"]
关于示例
此处的许多示例都假定存在文件 foo.txt 和 bar.txt。
$ cat foo.txt Foo 0 Foo 1 $ cat bar.txt Bar 0 Bar 1 Bar 2 Bar 3
ARGV 中的源
对于除了 最简单情况之外的任何 ARGF 读取(也就是说,除了第一次 ARGF 读取且 ARGV 为空的情况),源都可以在 ARGV 中找到。
ARGF 假定数组 ARGV 中的每个元素都是一个潜在的源,并且是以下之一:
-
文件的字符串路径,可以作为流打开。
-
字符
'-',表示流$stdin。
每个不是以上之一的元素在 ARGF 访问该源之前都应从 ARGV 中删除。
在以下示例中:
-
文件路径
foo.txt和bar.txt可以保留为潜在的源。 -
选项
--xyzzy和--mojo应该被删除。
示例
-
文件
t.rb# Print arguments (and options, if any) found on command line. p ['ARGV', ARGV]
-
命令和输出
$ ruby t.rb --xyzzy --mojo foo.txt bar.txt ["ARGV", ["--xyzzy", "--mojo", "foo.txt", "bar.txt"]]
ARGF 的流访问从左到右考虑 ARGV 的元素。
-
文件
t.rbp "ARGV: #{ARGV}" p "Read: #{ARGF.read}" # Read everything from all specified streams.
-
命令和输出
$ ruby t.rb foo.txt bar.txt "ARGV: [\"foo.txt\", \"bar.txt\"]" "Read: Foo 0\nFoo 1\nBar 0\nBar 1\nBar 2\nBar 3\n"
因为 ARGV 的值是一个普通数组,所以你可以操作它来控制 ARGF 考虑哪些源。
-
如果你从
ARGV中删除一个元素,ARGF 将不会考虑相应的源。 -
如果你向
ARGV添加一个元素,ARGF 将会考虑相应的源。
ARGV 中的每个元素在其相应的源被访问后都会被移除;当所有源都被访问后,该数组将为空。
-
文件
t.rbuntil ARGV.empty? && ARGF.eof? p "ARGV: #{ARGV}" p "Line: #{ARGF.readline}" # Read each line from each specified stream. end
-
命令和输出
$ ruby t.rb foo.txt bar.txt "ARGV: [\"foo.txt\", \"bar.txt\"]" "Line: Foo 0\n" "ARGV: [\"bar.txt\"]" "Line: Foo 1\n" "ARGV: [\"bar.txt\"]" "Line: Bar 0\n" "ARGV: []" "Line: Bar 1\n" "ARGV: []" "Line: Bar 2\n" "ARGV: []" "Line: Bar 3\n"
ARGV 中的文件路径
ARGV 数组可能包含指定 ARGF 读取源的文件路径。
此程序打印它从命令行指定路径的文件中读取的内容。
-
文件
t.rbp ['ARGV', ARGV] # Read and print all content from the specified sources. p ['ARGF.read', ARGF.read]
-
命令和输出
$ ruby t.rb foo.txt bar.txt ["ARGV", [foo.txt, bar.txt] ["ARGF.read", "Foo 0\nFoo 1\nBar 0\nBar 1\nBar 2\nBar 3\n"]
在 ARGV 中指定 $stdin
要在 ARGV 中指定流 $stdin,请使用字符 '-'。
-
文件
t.rbp ['ARGV', ARGV] p ['ARGF.read', ARGF.read]
-
命令和输出
$ echo "Open the pod bay doors, Hal." | ruby t.rb - ["ARGV", ["-"]] ["ARGF.read", "Open the pod bay doors, Hal.\n"]
当没有给出字符 '-' 时,流 $stdin 将被忽略(例外:请参见 在 ARGV 中指定 $stdin)。
-
命令和输出
$ echo "Open the pod bay doors, Hal." | ruby t.rb foo.txt bar.txt "ARGV: [\"foo.txt\", \"bar.txt\"]" "Read: Foo 0\nFoo 1\nBar 0\nBar 1\nBar 2\nBar 3\n"
ARGV 中的混合和重复
对于 ARGF 读取器,ARGV 可以包含任何文件路径和字符 '-' 的混合,包括重复。
对 ARGV 的修改
运行中的 Ruby 程序可以对 ARGV 数组进行任何修改;ARGV 的当前值会影响 ARGF 读取。
空 ARGV
对于空的 ARGV,ARGF 读取方法将根据具体方法返回 nil 或引发异常。
更多读取方法
如上所示,方法 ARGF#read 将所有源的内容读取到一个字符串中。其他 ARGF 方法提供了访问这些内容的其他方式,包括:
-
代码点访问:
each_codepoint。 -
源访问:
read,read_nonblock,readpartial。
关于 Enumerable
ARGF 包含了模块 Enumerable。Enumerable 中的几乎所有方法都会调用包含类中的 each 方法。
请注意:在 ARGF 中,方法 each 返回来自源的数据,而不是来自 ARGV;因此,例如,ARGF#entries 返回一个源行的数组,而不是 ARGV 中字符串的数组。
-
文件
t.rbp ['ARGV', ARGV] p ['ARGF.entries', ARGF.entries]
-
命令和输出
$ ruby t.rb foo.txt bar.txt ["ARGV", ["foo.txt", "bar.txt"]] ["ARGF.entries", ["Foo 0\n", "Foo 1\n", "Bar 0\n", "Bar 1\n", "Bar 2\n", "Bar 3\n"]]
写入
如果处于原地编辑模式,ARGF 可以写入目标流,这些流在任何特定时间都由 ARGV 的内容决定。
关于原地编辑模式的方法
写入方法
Public Instance Methods
Source
static VALUE
argf_argv(VALUE argf)
{
return ARGF.argv;
}
返回 ARGV 数组,其中包含传递给脚本的参数,每个参数占一个元素。
例如
$ ruby argf.rb -v glark.txt ARGF.argv #=> ["-v", "glark.txt"]
Source
static VALUE
argf_binmode_m(VALUE argf)
{
ARGF.binmode = 1;
next_argv();
ARGF_FORWARD(0, 0);
rb_io_ascii8bit_binmode(ARGF.current_file);
return argf;
}
Source
static VALUE
argf_binmode_p(VALUE argf)
{
return RBOOL(ARGF.binmode);
}
如果 ARGF 正在以二进制模式读取,则返回 true;否则返回 false。要启用二进制模式,请使用 ARGF.binmode。
例如
ARGF.binmode? #=> false ARGF.binmode ARGF.binmode? #=> true
Source
static VALUE
argf_close_m(VALUE argf)
{
next_argv();
argf_close(argf);
if (ARGF.next_p != -1) {
ARGF.next_p = 1;
}
ARGF.lineno = 0;
return argf;
}
关闭当前文件并跳到 ARGV 中的下一个文件。如果没有更多文件要打开,则只关闭当前文件。STDIN 不会关闭。
例如
$ ruby argf.rb foo bar ARGF.filename #=> "foo" ARGF.close ARGF.filename #=> "bar" ARGF.close
Source
static VALUE
argf_closed(VALUE argf)
{
next_argv();
ARGF_FORWARD(0, 0);
return rb_io_closed_p(ARGF.current_file);
}
返回true表示当前文件已关闭;false表示尚未关闭。使用 ARGF.close 来实际关闭当前文件。
Source
static VALUE
argf_each_line(int argc, VALUE *argv, VALUE argf)
{
RETURN_ENUMERATOR(argf, argc, argv);
FOREACH_ARGF() {
argf_block_call_line(rb_intern("each_line"), argc, argv, argf);
}
return argf;
}
返回一个迭代器,该迭代器遍历 ARGV 中每个文件的每一行(由sep分隔,sep默认为平台相关的换行符)。如果提供了块,则依次将每一行传递给块;否则返回一个迭代器。可选的limit参数是一个 Integer,指定每行的最大长度;较长的行将根据此限制进行拆分。
此方法允许您将命令行提供的文件视为一个单独的文件,由每个命名文件的连接组成。在返回第一个文件的最后一行后,将返回第二个文件的第一行。 ARGF.filename 和 ARGF.lineno 方法可用于确定当前行的文件名和整个输入的行号。
例如,以下代码打印出每个命名文件的每一行,并加上行号前缀,每个文件显示一次文件名:
ARGF.each_line do |line| puts ARGF.filename if ARGF.file.lineno == 1 puts "#{ARGF.file.lineno}: #{line}" end
而以下代码仅在开始时打印第一个文件名,并通过所有命名文件的行号计数来打印内容。
ARGF.each_line do |line| puts ARGF.filename if ARGF.lineno == 1 puts "#{ARGF.lineno}: #{line}" end
Source
static VALUE
argf_each_byte(VALUE argf)
{
RETURN_ENUMERATOR(argf, 0, 0);
FOREACH_ARGF() {
argf_block_call(rb_intern("each_byte"), 0, 0, argf);
}
return argf;
}
遍历 ARGV 中每个文件的每个字节。字节作为 0..255 范围内的 Integer 返回。
此方法允许您将命令行提供的文件视为一个单独的文件,由每个命名文件的连接组成。在返回第一个文件的最后一个字节后,将返回第二个文件的第一个字节。 ARGF.filename 方法可用于确定当前字节所在的文件名。
如果没有给出块,则返回一个迭代器。
例如
ARGF.bytes.to_a #=> [35, 32, ... 95, 10]
Source
static VALUE
argf_each_char(VALUE argf)
{
RETURN_ENUMERATOR(argf, 0, 0);
FOREACH_ARGF() {
argf_block_call(rb_intern("each_char"), 0, 0, argf);
}
return argf;
}
遍历 ARGF 中每个文件的每个字符。
此方法允许您将命令行提供的文件视为一个单独的文件,由每个命名文件的连接组成。在返回第一个文件的最后一个字符后,将返回第二个文件的第一个字符。 ARGF.filename 方法可用于确定当前字符所在的文件名。
如果没有给出块,则返回一个迭代器。
Source
static VALUE
argf_each_codepoint(VALUE argf)
{
RETURN_ENUMERATOR(argf, 0, 0);
FOREACH_ARGF() {
argf_block_call(rb_intern("each_codepoint"), 0, 0, argf);
}
return argf;
}
遍历 ARGF 中每个文件的每个代码点。
此方法允许您将命令行提供的文件视为一个单独的文件,由每个命名文件的连接组成。在返回第一个文件的最后一个代码点后,将返回第二个文件的第一个代码点。 ARGF.filename 方法可用于确定当前代码点所在的文件名。
如果没有给出块,则返回一个迭代器。
返回一个迭代器,该迭代器遍历 ARGV 中每个文件的每一行(由sep分隔,sep默认为平台相关的换行符)。如果提供了块,则依次将每一行传递给块;否则返回一个迭代器。可选的limit参数是一个 Integer,指定每行的最大长度;较长的行将根据此限制进行拆分。
此方法允许您将命令行提供的文件视为一个单独的文件,由每个命名文件的连接组成。在返回第一个文件的最后一行后,将返回第二个文件的第一行。 ARGF.filename 和 ARGF.lineno 方法可用于确定当前行的文件名和整个输入的行号。
例如,以下代码打印出每个命名文件的每一行,并加上行号前缀,每个文件显示一次文件名:
ARGF.each_line do |line| puts ARGF.filename if ARGF.file.lineno == 1 puts "#{ARGF.file.lineno}: #{line}" end
而以下代码仅在开始时打印第一个文件名,并通过所有命名文件的行号计数来打印内容。
ARGF.each_line do |line| puts ARGF.filename if ARGF.lineno == 1 puts "#{ARGF.lineno}: #{line}" end
Source
static VALUE
argf_eof(VALUE argf)
{
next_argv();
if (RTEST(ARGF.current_file)) {
if (ARGF.init_p == 0) return Qtrue;
next_argv();
ARGF_FORWARD(0, 0);
if (rb_io_eof(ARGF.current_file)) {
return Qtrue;
}
}
return Qfalse;
}
如果 ARGF 中的当前文件已到达文件末尾,则返回 true,即没有可供读取的数据。流必须以读取模式打开,否则将引发 IOError。
$ echo "eof" | ruby argf.rb
ARGF.eof? #=> false
3.times { ARGF.readchar }
ARGF.eof? #=> false
ARGF.readchar #=> "\n"
ARGF.eof? #=> true
如果 ARGF 中的当前文件已到达文件末尾,则返回 true,即没有可供读取的数据。流必须以读取模式打开,否则将引发 IOError。
$ echo "eof" | ruby argf.rb
ARGF.eof? #=> false
3.times { ARGF.readchar }
ARGF.eof? #=> false
ARGF.readchar #=> "\n"
ARGF.eof? #=> true
Source
static VALUE
argf_external_encoding(VALUE argf)
{
return argf_encoding(argf, rb_io_external_encoding);
}
将来自 ARGF 的文件外部编码作为 Encoding 对象返回。外部编码是文本在文件中存储时的编码。与 ARGF.internal_encoding 相对,后者是文本在 Ruby 中表示时使用的编码。
要设置外部编码,请使用 ARGF.set_encoding。
例如
ARGF.external_encoding #=> #<Encoding:UTF-8>
Source
static VALUE
argf_file(VALUE argf)
{
next_argv();
return ARGF.current_file;
}
Source
static VALUE
argf_filename(VALUE argf)
{
next_argv();
return ARGF.filename;
}
返回当前文件名。“-” 表示当前文件是 STDIN。
例如
$ echo "foo" > foo $ echo "bar" > bar $ echo "glark" > glark $ ruby argf.rb foo bar glark ARGF.filename #=> "foo" ARGF.read(5) #=> "foo\nb" ARGF.filename #=> "bar" ARGF.skip ARGF.filename #=> "glark"
Source
static VALUE
argf_fileno(VALUE argf)
{
if (!next_argv()) {
rb_raise(rb_eArgError, "no stream");
}
ARGF_FORWARD(0, 0);
return rb_io_fileno(ARGF.current_file);
}
返回一个表示当前文件数字文件描述符的整数。如果没有当前文件,则会引发 ArgumentError。
ARGF.fileno #=> 3
Source
static VALUE
argf_getbyte(VALUE argf)
{
VALUE ch;
retry:
if (!next_argv()) return Qnil;
if (!RB_TYPE_P(ARGF.current_file, T_FILE)) {
ch = forward_current(rb_intern("getbyte"), 0, 0);
}
else {
ch = rb_io_getbyte(ARGF.current_file);
}
if (NIL_P(ch) && ARGF.next_p != -1) {
argf_close(argf);
ARGF.next_p = 1;
goto retry;
}
return ch;
}
从 ARGF 获取下一个 8 位字节(0..255)。在流的末尾调用时返回 nil。
例如
$ echo "foo" > file $ ruby argf.rb file ARGF.getbyte #=> 102 ARGF.getbyte #=> 111 ARGF.getbyte #=> 111 ARGF.getbyte #=> 10 ARGF.getbyte #=> nil
Source
static VALUE
argf_getc(VALUE argf)
{
VALUE ch;
retry:
if (!next_argv()) return Qnil;
if (ARGF_GENERIC_INPUT_P()) {
ch = forward_current(rb_intern("getc"), 0, 0);
}
else {
ch = rb_io_getc(ARGF.current_file);
}
if (NIL_P(ch) && ARGF.next_p != -1) {
argf_close(argf);
ARGF.next_p = 1;
goto retry;
}
return ch;
}
Source
static VALUE
argf_gets(int argc, VALUE *argv, VALUE argf)
{
VALUE line;
line = argf_getline(argc, argv, argf);
rb_lastline_set(line);
return line;
}
从 ARGF 中的当前文件返回下一行。
默认情况下,行被假定为由 $/ 分隔;要使用不同的字符作为分隔符,请将其作为 String 提供给sep参数。
可选的limit参数指定每行返回多少个字符。默认情况下返回所有字符。
有关 getline_args 的详细信息,请参阅 IO.readlines。
Source
static VALUE
argf_inplace_mode_get(VALUE argf)
{
if (!ARGF.inplace) return Qnil;
if (NIL_P(ARGF.inplace)) return rb_str_new(0, 0);
return rb_str_dup(ARGF.inplace);
}
返回在原地编辑模式下附加到修改文件备份副本名称的文件扩展名。此值可以使用 ARGF.inplace_mode= 设置,或将 -i 开关传递给 Ruby 二进制文件。
Source
static VALUE
argf_inplace_mode_set(VALUE argf, VALUE val)
{
if (!RTEST(val)) {
ARGF.inplace = Qfalse;
}
else if (StringValueCStr(val), !RSTRING_LEN(val)) {
ARGF.inplace = Qnil;
}
else {
ARGF.inplace = rb_str_new_frozen(val);
}
return argf;
}
将原地编辑模式的文件名扩展名设置为给定的 String。正在编辑的每个文件的备份副本都将此值附加到其文件名。
例如
$ ruby argf.rb file.txt
ARGF.inplace_mode = '.bak'
ARGF.each_line do |line|
print line.sub("foo","bar")
end
首先,创建 file.txt.bak 作为 file.txt 的备份副本。然后,将 file.txt 的每一行中的第一个“foo”替换为“bar”。
Source
static VALUE
argf_internal_encoding(VALUE argf)
{
return argf_encoding(argf, rb_io_internal_encoding);
}
将来自 ARGF 的字符串的内部编码作为 Encoding 对象返回。
如果调用了 ARGF.set_encoding 并带有两个编码名称,则返回第二个。否则,如果设置了 Encoding.default_external,则返回该值。否则,如果命令行指定了默认外部编码,则使用该值。如果编码未知,则返回 nil。
Source
static VALUE
argf_lineno(VALUE argf)
{
return INT2FIX(ARGF.lineno);
}
返回 ARGF 作为一个整体的当前行号。此值可以通过 ARGF.lineno= 手动设置。
例如
ARGF.lineno #=> 0 ARGF.readline #=> "This is line 1\n" ARGF.lineno #=> 1
Source
static VALUE
argf_set_lineno(VALUE argf, VALUE val)
{
ARGF.lineno = NUM2INT(val);
ARGF.last_lineno = ARGF.lineno;
return val;
}
将 ARGF 作为一个整体的行号设置为给定的 Integer。
ARGF 在您读取数据时会自动设置行号,因此通常您不需要显式设置它。要访问当前行号,请使用 ARGF.lineno。
例如
ARGF.lineno #=> 0 ARGF.readline #=> "This is line 1\n" ARGF.lineno #=> 1 ARGF.lineno = 0 #=> 0 ARGF.lineno #=> 0
返回当前文件名。“-” 表示当前文件是 STDIN。
例如
$ echo "foo" > foo $ echo "bar" > bar $ echo "glark" > glark $ ruby argf.rb foo bar glark ARGF.filename #=> "foo" ARGF.read(5) #=> "foo\nb" ARGF.filename #=> "bar" ARGF.skip ARGF.filename #=> "glark"
返回 ARGF 中当前文件的当前偏移量(以字节为单位)。
ARGF.pos #=> 0 ARGF.gets #=> "This is line one\n" ARGF.pos #=> 17
Source
static VALUE
argf_set_pos(VALUE argf, VALUE offset)
{
if (!next_argv()) {
rb_raise(rb_eArgError, "no stream to set position");
}
ARGF_FORWARD(1, &offset);
return rb_io_set_pos(ARGF.current_file, offset);
}
根据whence的值,在 ARGF 中将文件定位到position(以字节为单位)给定的偏移量。
例如
ARGF.pos = 17 ARGF.gets #=> "This is line two\n"
Source
VALUE
rb_io_print(int argc, const VALUE *argv, VALUE out)
{
int i;
VALUE line;
/* if no argument given, print `$_' */
if (argc == 0) {
argc = 1;
line = rb_lastline_get();
argv = &line;
}
if (argc > 1 && !NIL_P(rb_output_fs)) {
rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "$, is set to non-nil value");
}
for (i=0; i<argc; i++) {
if (!NIL_P(rb_output_fs) && i>0) {
rb_io_write(out, rb_output_fs);
}
rb_io_write(out, argv[i]);
}
if (argc > 0 && !NIL_P(rb_output_rs)) {
rb_io_write(out, rb_output_rs);
}
return Qnil;
}
将给定对象写入流;返回 nil。如果输出记录分隔符 $OUTPUT_RECORD_SEPARATOR ($\) 不为 nil,则会追加它。请参阅 行 IO。
带参数 objects 时,对于每个对象
-
如果不是字符串,则通过其方法
to_s进行转换。 -
写入流。
-
如果不是最后一个对象,则写入输出字段分隔符
$OUTPUT_FIELD_SEPARATOR($,)(如果它不为nil)。
带默认分隔符
f = File.open('t.tmp', 'w+') objects = [0, 0.0, Rational(0, 1), Complex(0, 0), :zero, 'zero'] p $OUTPUT_RECORD_SEPARATOR p $OUTPUT_FIELD_SEPARATOR f.print(*objects) f.rewind p f.read f.close
输出
nil nil "00.00/10+0izerozero"
带指定分隔符
$\ = "\n" $, = ',' f.rewind f.print(*objects) f.rewind p f.read
输出
"0,0.0,0/1,0+0i,zero,zero\n"
不带参数时,写入 $_ 的内容(通常是最近的用户输入)。
f = File.open('t.tmp', 'w+') gets # Sets $_ to the most recent user input. f.print f.close
Source
VALUE
rb_io_printf(int argc, const VALUE *argv, VALUE out)
{
rb_io_write(out, rb_f_sprintf(argc, argv));
return Qnil;
}
格式化并将objects写入流。
有关 format_string 的详细信息,请参阅 格式规范。
Source
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;
}
将字符写入流。请参阅 字符 IO。
如果object是数值,则在必要时将其转换为整数,然后写入其最低有效字节为代码的字符;如果object是字符串,则写入第一个字符。
$stdout.putc "A" $stdout.putc 65
输出
AA
Source
VALUE
rb_io_puts(int argc, const VALUE *argv, VALUE out)
{
VALUE line, args[2];
/* if no argument given, print newline. */
if (argc == 0) {
rb_io_write(out, rb_default_rs);
return Qnil;
}
for (int i = 0; i < argc; i++) {
// Convert the argument to a string:
if (RB_TYPE_P(argv[i], T_STRING)) {
line = argv[i];
}
else if (rb_exec_recursive(io_puts_ary, argv[i], out)) {
continue;
}
else {
line = rb_obj_as_string(argv[i]);
}
// Write the line:
int n = 0;
if (RSTRING_LEN(line) == 0) {
args[n++] = rb_default_rs;
}
else {
args[n++] = line;
if (!rb_str_end_with_asciichar(line, '\n')) {
args[n++] = rb_default_rs;
}
}
rb_io_writev(out, n, args);
}
return Qnil;
}
将给定的objects写入流,该流必须打开以供写入;返回 nil。\在每个字符串后面添加一个换行符,如果它还不以换行序列结尾。如果省略参数,则写入一个换行符。请参阅 行 IO。
请注意,每个添加的换行符都是字符 "\n",而不是输出记录分隔符 ($\)。
每个对象的处理
-
字符串:写入字符串。
-
不是字符串也不是数组:写入
object.to_s。 -
数组:写入数组的每个元素;数组可以嵌套。
为使这些示例保持简短,我们定义了此辅助方法:
def show(*objects) # Puts objects to file. f = File.new('t.tmp', 'w+') f.puts(objects) # Return file content. f.rewind p f.read f.close end # Strings without newlines. show('foo', 'bar', 'baz') # => "foo\nbar\nbaz\n" # Strings, some with newlines. show("foo\n", 'bar', "baz\n") # => "foo\nbar\nbaz\n" # Neither strings nor arrays: show(0, 0.0, Rational(0, 1), Complex(9, 0), :zero) # => "0\n0.0\n0/1\n9+0i\nzero\n" # Array of strings. show(['foo', "bar\n", 'baz']) # => "foo\nbar\nbaz\n" # Nested arrays. show([[[0, 1], 2, 3], 4, 5]) # => "0\n1\n2\n3\n4\n5\n"
Source
static VALUE
argf_read(int argc, VALUE *argv, VALUE argf)
{
VALUE tmp, str, length;
long len = 0;
rb_scan_args(argc, argv, "02", &length, &str);
if (!NIL_P(length)) {
len = NUM2LONG(argv[0]);
}
if (!NIL_P(str)) {
StringValue(str);
rb_str_resize(str,0);
argv[1] = Qnil;
}
retry:
if (!next_argv()) {
return str;
}
if (ARGF_GENERIC_INPUT_P()) {
tmp = argf_forward(argc, argv, argf);
}
else {
tmp = io_read(argc, argv, ARGF.current_file);
}
if (NIL_P(str)) str = tmp;
else if (!NIL_P(tmp)) rb_str_append(str, tmp);
if (NIL_P(tmp) || NIL_P(length)) {
if (ARGF.next_p != -1) {
argf_close(argf);
ARGF.next_p = 1;
goto retry;
}
}
else if (argc >= 1) {
long slen = RSTRING_LEN(str);
if (slen < len) {
argv[0] = LONG2NUM(len - slen);
goto retry;
}
}
return str;
}
从 ARGF 读取length字节。命令行中命名的文件被连接起来,并被此方法视为一个单一文件,因此当不带参数调用时,此伪文件的内容将全部返回。
length必须是一个非负整数或 nil。
如果length是一个正整数,read会尝试读取length个字节而不进行任何转换(二进制模式)。如果读到任何内容之前遇到 EOF,则返回 nil。如果读操作期间遇到 EOF,则返回的字节数少于length。对于整数length,结果字符串的编码始终为 ASCII-8BIT。
如果省略length或其值为 nil,则读取直到 EOF,并应用编码转换(如果适用)。即使在读取任何数据之前遇到 EOF,也会返回一个字符串。
如果length为零,则返回一个空字符串 ("")。
如果提供了可选的outbuf参数,它必须引用一个 String,数据将存储在该字符串中。调用方法后,outbuf将仅包含收到的数据,即使它开始时不为空。
例如
$ echo "small" > small.txt $ echo "large" > large.txt $ ./glark.rb small.txt large.txt ARGF.read #=> "small\nlarge" ARGF.read(200) #=> "small\nlarge" ARGF.read(2) #=> "sm" ARGF.read(0) #=> ""
请注意,此方法类似于 C 语言中的 fread() 函数。这意味着它会尝试使用 read(2) 系统调用来读取指定长度的数据。如果您需要类似单个 read(2) 系统调用的行为,请考虑 ARGF#readpartial 或 ARGF#read_nonblock。
Source
static VALUE
argf_read_nonblock(int argc, VALUE *argv, VALUE argf)
{
VALUE opts;
rb_scan_args(argc, argv, "11:", NULL, NULL, &opts);
if (!NIL_P(opts))
argc--;
return argf_getpartial(argc, argv, argf, opts, 1);
}
以非阻塞模式从 ARGF 流中读取最多maxlen个字节。
Source
static VALUE
argf_readbyte(VALUE argf)
{
VALUE c;
NEXT_ARGF_FORWARD(0, 0);
c = argf_getbyte(argf);
if (NIL_P(c)) {
rb_eof_error();
}
return c;
}
Source
static VALUE
argf_readchar(VALUE argf)
{
VALUE ch;
retry:
if (!next_argv()) rb_eof_error();
if (!RB_TYPE_P(ARGF.current_file, T_FILE)) {
ch = forward_current(rb_intern("getc"), 0, 0);
}
else {
ch = rb_io_getc(ARGF.current_file);
}
if (NIL_P(ch) && ARGF.next_p != -1) {
argf_close(argf);
ARGF.next_p = 1;
goto retry;
}
return ch;
}
Source
static VALUE
argf_readline(int argc, VALUE *argv, VALUE argf)
{
VALUE line;
if (!next_argv()) rb_eof_error();
ARGF_FORWARD(argc, argv);
line = argf_gets(argc, argv, argf);
if (NIL_P(line)) {
rb_eof_error();
}
return line;
}
Source
static VALUE
argf_readlines(int argc, VALUE *argv, VALUE argf)
{
long lineno = ARGF.lineno;
VALUE lines, ary;
ary = rb_ary_new();
while (next_argv()) {
if (ARGF_GENERIC_INPUT_P()) {
lines = forward_current(rb_intern("readlines"), argc, argv);
}
else {
lines = rb_io_readlines(argc, argv, ARGF.current_file);
argf_close(argf);
}
ARGF.next_p = 1;
rb_ary_concat(ary, lines);
ARGF.lineno = lineno + RARRAY_LEN(ary);
ARGF.last_lineno = ARGF.lineno;
}
ARGF.init_p = 0;
return ary;
}
读取 ARGF 中的每个文件并返回一个包含这些文件行的 Array。行被假定由sep分隔。
lines = ARGF.readlines lines[0] #=> "This is line one\n"
有关所有选项的完整说明,请参阅 IO.readlines。
Source
static VALUE
argf_readpartial(int argc, VALUE *argv, VALUE argf)
{
return argf_getpartial(argc, argv, argf, Qnil, 0);
}
Source
static VALUE
argf_rewind(VALUE argf)
{
VALUE ret;
int old_lineno;
if (!next_argv()) {
rb_raise(rb_eArgError, "no stream to rewind");
}
ARGF_FORWARD(0, 0);
old_lineno = RFILE(ARGF.current_file)->fptr->lineno;
ret = rb_io_rewind(ARGF.current_file);
if (!global_argf_p(argf)) {
ARGF.last_lineno = ARGF.lineno -= old_lineno;
}
return ret;
}
将当前文件定位到输入的开头,并将 ARGF.lineno 重置为零。
ARGF.readline #=> "This is line one\n" ARGF.rewind #=> 0 ARGF.lineno #=> 0 ARGF.readline #=> "This is line one\n"
Source
static VALUE
argf_seek_m(int argc, VALUE *argv, VALUE argf)
{
if (!next_argv()) {
rb_raise(rb_eArgError, "no stream to seek");
}
ARGF_FORWARD(argc, argv);
return rb_io_seek_m(argc, argv, ARGF.current_file);
}
Source
static VALUE
argf_set_encoding(int argc, VALUE *argv, VALUE argf)
{
rb_io_t *fptr;
if (!next_argv()) {
rb_raise(rb_eArgError, "no stream to set encoding");
}
rb_io_set_encoding(argc, argv, ARGF.current_file);
GetOpenFile(ARGF.current_file, fptr);
ARGF.encs = fptr->encs;
return argf;
}
如果指定了单个参数,则从 ARGF 读取的字符串将使用指定的编码进行标记。
如果提供了两个用冒号分隔的编码名称,例如“ascii:utf-8”,则读取的字符串将从第一个编码(外部编码)转换为第二个编码(内部编码),然后用第二个编码进行标记。
如果提供了两个参数,它们必须是编码对象或编码名称。第一个指定外部编码;第二个指定内部编码。
如果指定了外部编码和内部编码,则可以使用可选的 Hash 参数来调整转换过程。此哈希的结构在 String#encode 文档中有说明。
例如
ARGF.set_encoding('ascii') # Tag the input as US-ASCII text ARGF.set_encoding(Encoding::UTF_8) # Tag the input as UTF-8 text ARGF.set_encoding('utf-8','ascii') # Transcode the input from US-ASCII # to UTF-8.
Source
static VALUE
argf_skip(VALUE argf)
{
if (ARGF.init_p && ARGF.next_p == 0) {
argf_close(argf);
ARGF.next_p = 1;
}
return argf;
}
将当前文件设置为 ARGV 中的下一个文件。如果没有更多文件,则此操作无效。
例如
$ ruby argf.rb foo bar ARGF.filename #=> "foo" ARGF.skip ARGF.filename #=> "bar"
Source
static VALUE
argf_tell(VALUE argf)
{
if (!next_argv()) {
rb_raise(rb_eArgError, "no stream to tell");
}
ARGF_FORWARD(0, 0);
return rb_io_tell(ARGF.current_file);
}
返回 ARGF 中当前文件的当前偏移量(以字节为单位)。
ARGF.pos #=> 0 ARGF.gets #=> "This is line one\n" ARGF.pos #=> 17
读取 ARGF 中的每个文件并返回一个包含这些文件行的 Array。行被假定由sep分隔。
lines = ARGF.readlines lines[0] #=> "This is line one\n"
有关所有选项的完整说明,请参阅 IO.readlines。
Source
static VALUE
argf_to_io(VALUE argf)
{
next_argv();
ARGF_FORWARD(0, 0);
return ARGF.current_file;
}
Source
static VALUE
argf_write_io(VALUE argf)
{
if (!RTEST(ARGF.current_file)) {
rb_raise(rb_eIOError, "not opened for writing");
}
return GetWriteIO(ARGF.current_file);
}
在启用原地编辑模式时,返回与ARGF关联的用于写入的 IO 实例。
Source
static VALUE
argf_write(int argc, VALUE *argv, VALUE argf)
{
return rb_io_writev(argf_write_io(argf), argc, argv);
}
在原地编辑模式下写入每个给定的objects。