Ruby: comprimento de uma linha de um arquivo em bytes?
Pergunta
Eu estou escrevendo este pequeno HelloWorld como uma continuação a este e os números não somam
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}"
O resultado não é o mesmo que o tamanho do arquivo. Eu acho que só precisa saber o que format
eu preciso ligar ... ou talvez eu perdi completamente o ponto. Como posso medir a linha de tamanho de arquivo por linha?
Nota:. Eu estou no Windows, eo ficheiro é codificado como tipo ANSI
Editar: Isso produz os mesmos resultados
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}"
para qualquer um que pode ajudar agora ...
Solução
IO # fica funciona da mesma como se estivesse a captura de entrada da linha de comando: o "Enter" não é enviado como parte da entrada; nem é passado quando #gets é chamado em um arquivo ou outra subclasse de IO, por isso os números são definitivamente não vai igualar-se.
Veja a Picareta
Posso perguntar por que você está tão preocupado com a comprimentos de linha somando o tamanho do arquivo? Você pode estar resolvendo um problema mais difícil do que o necessário ...
Aha. Eu acho que eu entendo agora.
Na falta de um iPod à mão (ou qualquer outro tipo, para que o assunto), eu não sei se você quer exatamente 4K pedaços, caso em que IO # ler (4000) seria seu amigo (4000 ou 4096?) Ou se você é mais feliz para quebrar a linha, caso em que algo como isto deve funcionar:
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
Observe o uso de IO # readlines para obter todo o texto em um gole: #each ou #each_line faria bem. Eu costumava String # chomp! para garantir que tudo o que o sistema operacional está fazendo, as byts no final são removidos, de modo que \ n ou o que pode ser forçado na saída.
Gostaria de sugerir usando Arquivo nº escrita, ao invés de #print ou #puts para a saída, como estes têm uma tendência para entregar sequências de nova linha específicas do OS.
Se você está realmente preocupado com caracteres multi-byte, considere tomar o each_byte ou descompactar (C *) opções e-monkey patching String, algo como isto:
class String
def size_in_bytes
self.unpack("C*").size
end
end
A versão descompactar é de cerca de 8 vezes mais rápido do que o each_byte na minha máquina, btw.
Outras dicas
Você pode tentar IO # each_byte, por exemplo.
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}"
Isso, é claro, não lhe dá uma linha de cada vez. Sua melhor opção para isso é, provavelmente, para percorrer o arquivo via each_byte
até encontrar \r\n
. A classe IO fornece um monte de métodos de leitura de baixo nível bonita que pode ser útil.
Você potencialmente tem vários problemas de sobreposição aqui:
-
caracteres de avanço de linha
\r\n
vs.\n
(como por seu post anterior). Também caráter arquivo EOF (^ Z)? -
A definição de "tamanho" em sua declaração do problema: você quer dizer "quantos caracteres" (levando em codificações conta de caracteres multi-byte) ou você quer dizer "quantos bytes"
? li> -
A interação da variável global
$KCODE
(reprovado no Ruby 1.9. VejaString#encoding
e amigos se você estiver executando sob 1.9). Há, por exemplo, caracteres acentuados no seu arquivo? -
A seqüência de formato para
#unpack
. Eu acho que você querC*
aqui se você realmente quer contar bytes.
Note-se também a existência de IO#each_line
(só assim você pode jogar fora o while
e ser um pouco mais ruby-idiomática; -)).
O problema é que quando você salva um arquivo de texto em janelas, suas quebras de linha são dois caracteres (caracteres 13 e 10) e, portanto, 2 bytes, quando você salva-lo no Linux há apenas 1 (caráter 10). No entanto, os relatórios de rubi tanto estes como um único caractere '\ n' -. Diz caráter 10. O que é pior, é que se você estiver no Linux com um arquivo de janelas, rubi lhe dará ambos os personagens
Então, se você sabe que seus arquivos estão vindo sempre a partir de arquivos de texto do Windows e executado no Windows, toda vez que receber um caractere de nova linha você pode adicionar 1 à sua contagem. Caso contrário, é um par de condicionais e um pouco de máquina de estado.
BTW há 'caráter' não EOF.
f = File.new("log.txt")
begin
while (line = f.readline)
line.chomp
puts line.length
end
rescue EOFError
f.close
end
Aqui é uma solução simples, presumindo que o ponteiro do arquivo atual é definida como o início de uma linha no arquivo de leitura:
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)
Neste exemplo "file" é o arquivo a partir do qual você está lendo. Para fazer isso em um loop:
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