【问题标题】:Ruby: Length of a line of a file in bytes?Ruby:文件行的长度(以字节为单位)?
【发布时间】:2013-04-02 07:26:35
【问题描述】:

我正在写这个小 HelloWorld 作为this 的后续,但数字不加起来

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}"

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

【问题讨论】:

    标签: ruby byte


    【解决方案1】:

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

    查看相关Pickaxe section

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

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

    缺少方便的 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 倍,顺便说一句。

    【讨论】:

    • 可能过于复杂。我只是在换行符处将一个文件分解为 4K 片段以供 iPod 笔记使用。该程序有效,但已针对 Windows 进行了调整。 file.each 不是正确的方法吗?
    • 啊,我明白了 - 很高兴看到 Apple 提供高质量的体验。我认为可能有一种更简洁的方法来处理行终止的事情——我会去玩一点。
    • 非常感谢迈克。里面有很多文字,我必须尽快完成。
    • 迈克,我很确定那一口!对 File.each 中读取的行没有任何作用,因为它们已经丢失了行尾字符。
    • 我添加了 chomp!因为当创建文件的操作系统和处理文件的操作系统不同时,行尾及其处理的不确定性。在 Windows 中处理 *nix 创建的文件时发生了一些奇怪的事情。如果你不需要它,忽略它!
    【解决方案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 类提供了一堆非常低级的读取方法,这些方法可能会有所帮助。

    【讨论】:

    • @Sarah,无论我从什么文件格式/编码中读取,\r\n 是否总是存在?
    • 如果它在开始的文件中,它应该通过 read_bytes 存在。该方法适用于原始数据访问,而 readline 对行尾进行了一些修改。行尾将是 \n 或 \r\n,具体取决于文件的保存方式。
    • 抱歉打扰了,但你的意思是,它要么是 \n 要么是 \r\n 并且这些基本上是所有可能性?还是 file.each 处理的更多?
    • 转念一想,这只是出于好奇。我想如果我有 Linux、OSX 和 Windows,我都很好:)
    • 这将是他们中的大多数,但还有其他人。维基百科有一个非常全面的列表。 en.wikipedia.org/wiki/Newline
    【解决方案3】:

    您可能会遇到几个重叠的问题:

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

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

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

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

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

    【讨论】:

    • Martin,也许这是不可能的,但我只希望最后的两个 put 给它们相同的长度。 C* 也不起作用。我怎么知道 Windows ANSI 中的换行符有多长?
    • 你能提供一个最小问题文件(或行)的十六进制转储吗? #length on "68 65 6c 6c 6f 0d 0a" 在这里为我返回长度 7(正确!),所以我不确定您的问题出在哪里。
    • 嗨,马丁,1) 我已将代码修复为更符合 ruby​​ 习惯。 2) 文件现在位于confusionstudio.com/random/testThis.txt
    • 我没有看到这个文件有什么奇怪的地方(UNIX 换行符)。我在 Linux 或 Windows 上使用 File#size 或添加 line #lengths 得到 20061 个字节。
    【解决方案4】:

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

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

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

    【讨论】:

    • 是的,所以基本上我的选择是:1)每次都写一个小测试文件并检查它的大小或 2)读取一个文件并将行大小与文件大小进行比较,然后从那里更正(我已经尝试过了)。
    【解决方案5】:
    f = File.new("log.txt")
    begin
        while (line = f.readline)
            line.chomp
            puts line.length
        end
    rescue EOFError
        f.close
    end
    

    【讨论】:

    • 欢迎爱德华多!可以简化: File.new('log.txt').each_line { |line| puts line.length } (你对 line.chomp 的调用什么也没做?)
    • 谢谢爱德华多。 line.length 用于字符,所以它也不起作用。这是前面的问题。
    【解决方案6】:

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

        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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-05-07
      • 2013-06-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-05
      • 2014-08-09
      相关资源
      最近更新 更多