1

我正在写这个小 HelloWorld 作为对此的后续,并且数字不加起来

filename = "testThis.txt"
total_bytes = 0
file = File.new(filename, "r")
file.each do |line|
  total_bytes += line.unpack("U*").length
end
puts "original size #{File.size(filename)}"
puts "Total bytes #{total_bytes}"

结果与文件大小不同。我想我只需要知道format我需要插入什么......或者我可能完全错过了这一点。如何逐行测量文件大小?

注意:我在 Windows 上,文件被编码为 ANSI 类型。

编辑:这会产生相同的结果!

filename = "testThis.txt"
total_bytes = 0
file = File.new(filename, "r")
file.each_byte do |whatever|
  total_bytes += 1
end
puts "Original size #{File.size(filename)}"
puts "Total bytes #{total_bytes}"

所以现在任何可以提供帮助的人...

4

6 回答 6

2

您可以尝试 IO#each_byte,例如

total_bytes = 0
file_name = "test_this.txt"
File.open(file_name, "r") do |file|
  file.each_byte {|b| total_bytes += 1}
end
puts "Original size #{File.size(file_name)}"
puts "Total bytes #{total_bytes}"

当然,这不会一次给你一行。你最好的选择可能是通过文件each_byte直到你遇到\r\n. IO 类提供了许多可能有用的非常低级的读取方法。

于 2009-03-09T23:32:48.270 回答
2

IO#gets 的工作方式与从命令行捕获输入相同:“Enter”不作为输入的一部分发送;在 File 或 IO 的其他子类上调用 #gets 时也不会通过,因此这些数字肯定不会匹配。

请参阅相关的镐部分

我可以问一下您为什么如此担心行长与文件大小的总和吗?你可能正在解决一个比必要的更难的问题......

啊哈。我想我现在明白了。

缺少一个方便的 iPod(或任何其他类型,就此而言),我不知道您是否想要精确的 4K 块,在这种情况下 IO#read(4000) 将是您的朋友(4000 或 4096?)或者如果您更乐意按行打破,在这种情况下,这样的事情应该可以工作:

class Chunkifier
  def Chunkifier.to_chunks(path)
    chunks, current_chunk_size = [""], 0
    File.readlines(path).each do |line|
      line.chomp! # strips off \n, \r or \r\n depending on OS
      if chunks.last.size + line.size >= 4_000 # 4096?
        chunks.last.chomp! # remove last line terminator
        chunks << ""
      end
      chunks.last << line + "\n" # or whatever terminator you need
    end
    chunks
  end
end

if __FILE__ == $0
  require 'test/unit'
  class TestFile < Test::Unit::TestCase
    def test_chunking
      chs = Chunkifier.to_chunks(PATH)
      chs.each do |chunk|
        assert 4_000 >= chunk.size, "chunk is #{chunk.size} bytes long"
      end
    end
  end
end

请注意使用 IO#readlines 将所有文本一次性获取:#each 或 #each_line 也可以。我用过字符串#chomp!以确保无论操作系统在做什么,最后的字节都被删除,以便 \n 或任何可以强制进入输出。

我建议使用 File#write,而不是 #print 或 #puts 作为输出,因为后者倾向于提供特定于操作系统的换行符序列。

如果您真的关心多字节字符,请考虑使用 each_byte 或 unpack(C*) 选项和猴子修补字符串,如下所示:

class String
  def size_in_bytes
    self.unpack("C*").size
  end
end

顺便说一句,解包版本比我机器上的 each_byte 快大约 8 倍。

于 2009-03-10T00:07:37.703 回答
1

您可能在这里遇到几个重叠的问题:

  1. 换行字符\r\n\n(根据您之前的帖子)。还有EOF文件字符(^Z)?

  2. 问题陈述中“大小”的定义:您的意思是“多少个字符”(考虑到多字节字符编码)还是“多少个字节”?

  3. $KCODE全局变量 的交互(在 ruby​​ 1.9 中已弃用。String#encoding如果您在 1.9 下运行,请查看和朋友)。例如,您的文件中是否有重音字符?

  4. 您的格式字符串#unpack. C*如果你真的想计算字节数,我想你想在这里。

还要注意IO#each_line(只是这样你就可以扔掉while并变得更加 ruby​​ 惯用 ;-)) 的存在。

于 2009-03-09T21:51:39.980 回答
1

问题是当您在 Windows 上保存文本文件时,您的换行符是两个字符(字符 13 和 10),因此是 2 个字节,当您将它保存在 linux 上时只有 1 个(字符 10)。但是,ruby 将这两个字符都报告为单个字符 '\n' - 它表示字符 10。更糟糕的是,如果您在带有 windows 文件的 linux 上,ruby 会给您两个字符。

因此,如果您知道您的文件始终来自 Windows 文本文件并在 Windows 上执行,则每次获得换行符时,您都可以将计数加 1。否则,它是几个条件和一个小状态机。

顺便说一句,没有 EOF '字符'。

于 2009-03-11T06:43:29.367 回答
0
f = File.new("log.txt")
begin
    while (line = f.readline)
        line.chomp
        puts line.length
    end
rescue EOFError
    f.close
end
于 2009-03-09T21:38:08.517 回答
0

这是一个简单的解决方案,假设当前文件指针设置为读取文件的行首:

    last_pos = file.pos
    next_line = file.gets
    current_pos = file.pos
    backup_dist = last_pos - current_pos
    file.seek(backup_dist, IO::SEEK_CUR)

在此示例中,“文件”是您正在读取的文件。要在循环中执行此操作:

    last_pos = file.pos
    begin loop
        next_line = file.gets
        current_pos = file.pos
        backup_dist = last_pos - current_pos
        last_pos = current_pos
        file.seek(backup_dist, IO::SEEK_CUR)
    end loop
于 2013-03-30T21:42:05.870 回答