Вопрос

Я пишу этот небольшой 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}"

так что кто-нибудь, кто может помочь сейчас...

Это было полезно?

Решение

IO#gets работает так же, как если бы вы захватывали ввод из командной строки:«Ввод» не отправляется как часть ввода;он также не передается, когда #gets вызывается для файла или другого подкласса IO, поэтому числа определенно не будут совпадать.

См. соответствующие Раздел кирки

Могу я узнать, почему вас так беспокоит сумма длин строк и размер файла?Возможно, вы решаете более сложную задачу, чем необходимо...

Ага.Думаю, теперь я понял.

Не имея удобного iPod (или любого другого типа, если уж на то пошло), я не знаю, нужны ли вам именно куски размером 4 КБ, и в этом случае 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 тоже подойдут.Я использовал String#chomp!чтобы гарантировать, что что бы ни делала ОС, байты в конце удаляются, чтобы или что-то еще можно было принудительно вывести на вывод.

Я бы предложил использовать File#write вместо #print или #puts для вывода, поскольку последние имеют тенденцию доставлять последовательности новой строки, специфичные для ОС.

Если вас действительно беспокоят многобайтовые символы, рассмотрите возможность использования параметровeach_byte или unpack(C*) и исправления строк обезьяны, что-то вроде этого:

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

Кстати, распакованная версия примерно в 8 раз быстрее, чем версияeach_byte на моей машине.

Другие советы

Вы можете попробовать 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 предоставляет кучу довольно низкоуровневых методов чтения, которые могут быть полезны.

Здесь у вас потенциально может возникнуть несколько пересекающихся проблем:

  1. Символы перевода строки \r\n против. \n (согласно вашему предыдущему сообщению).Также символ файла EOF (^Z)?

  2. Определение «размера» в вашей постановке задачи:вы имеете в виду «сколько символов» (с учетом многобайтовых кодировок символов) или «сколько байтов»?

  3. Взаимодействие $KCODE глобальная переменная (устарела в Ruby 1.9.Видеть String#encoding и друзья, если вы используете версию 1.9).Есть ли в вашем файле, например, символы с диакритическими знаками?

  4. Ваша строка формата для #unpack.Я думаю, ты хочешь C* здесь, если вы действительно хотите считать байты.

Отметим также существование IO#each_line (просто чтобы вы могли выбросить while и будьте немного более рубиново-идиоматичными ;-)).

Проблема в том, что когда вы сохраняете текстовый файл в Windows, ваши разрывы строк составляют два символа (символы 13 и 10) и, следовательно, 2 байта, когда вы сохраняете его в Linux, там только 1 (символ 10).Однако Ruby сообщает об обоих как об одном символе ' ' — он обозначает символ 10.Что еще хуже, если вы используете Linux с файлом Windows, Ruby предоставит вам оба символа.

Итак, если вы знать что ваши файлы всегда поступают из текстовых файлов Windows и выполняются в Windows, каждый раз, когда вы получаете символ новой строки, вы можете добавить 1 к своему счетчику.В противном случае это пара условных операторов и немного конечного автомата.

Кстати, здесь нет «персонажа» EOF.

f = File.new("log.txt")
begin
    while (line = f.readline)
        line.chomp
        puts line.length
    end
rescue EOFError
    f.close
end

Вот простое решение, предполагающее, что указатель текущего файла установлен на начало строки в прочитанном файле:

    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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top