Рубин:Длина строки файла в байтах?
Вопрос
Я пишу этот небольшой 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 предоставляет кучу довольно низкоуровневых методов чтения, которые могут быть полезны.
Здесь у вас потенциально может возникнуть несколько пересекающихся проблем:
Символы перевода строки
\r\n
против.\n
(согласно вашему предыдущему сообщению).Также символ файла EOF (^Z)?Определение «размера» в вашей постановке задачи:вы имеете в виду «сколько символов» (с учетом многобайтовых кодировок символов) или «сколько байтов»?
Взаимодействие
$KCODE
глобальная переменная (устарела в Ruby 1.9.ВидетьString#encoding
и друзья, если вы используете версию 1.9).Есть ли в вашем файле, например, символы с диакритическими знаками?Ваша строка формата для
#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