Ruby: длина строки файла в байтах? - PullRequest
0 голосов
/ 10 марта 2009

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

так что любой, кто может помочь сейчас ...

Ответы [ 6 ]

2 голосов
/ 10 марта 2009

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

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

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

Aha. Я думаю, я понял это сейчас.

Не имея удобного 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 # для получения всего текста в одной лоскутке: #each или #each_line также подойдут. Я использовал String # chomp! чтобы гарантировать, что независимо от того, что делает ОС, байты в конце удаляются, чтобы \ n или что-либо еще можно было принудительно ввести в вывод.

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

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

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

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

2 голосов
/ 10 марта 2009

Вы можете попробовать 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 голос
/ 11 марта 2009

Проблема в том, что когда вы сохраняете текстовый файл в Windows, разрывы строк составляют два символа (символы 13 и 10) и, следовательно, 2 байта, при сохранении его в linux остается только 1 (символ 10). Тем не менее, ruby ​​сообщает обо всем этом как один символ '\ n' - он говорит о символе 10. Что еще хуже, если вы используете Linux с файлом Windows, ruby ​​выдаст вам оба символа.

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

КСТАТИ нет EOF 'персонажа'.

1 голос
/ 10 марта 2009

Возможно, у вас есть несколько проблем с перекрытием:

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

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

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

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

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

0 голосов
/ 31 марта 2013

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

    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
0 голосов
/ 10 марта 2009
f = File.new("log.txt")
begin
    while (line = f.readline)
        line.chomp
        puts line.length
    end
rescue EOFError
    f.close
end
...