Ruby: lunghezza di una riga di un file in byte?
Domanda
Sto scrivendo questo piccolo HelloWorld come seguito di questo e i numeri non si sommano
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}"
Il risultato non è uguale alla dimensione del file. Penso di aver solo bisogno di sapere quale formato
devo collegare ... o forse ho perso del tutto il punto. Come posso misurare le dimensioni del file riga per riga?
Nota: sono su Windows e il file è codificato come tipo ANSI.
Modifica: questo produce gli stessi risultati!
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}"
quindi chiunque possa aiutare adesso ...
Soluzione
IO # funziona allo stesso modo di quando stavi acquisendo input dalla riga di comando: il " Enter " non viene inviato come parte dell'input; né viene passato quando #gets viene chiamato su un File o su un'altra sottoclasse di IO, quindi i numeri sicuramente non coincideranno.
Consulta la sezione Pickaxe
pertinentePosso chiederti perché sei così preoccupato per la lunghezza della linea che si somma alla dimensione del file? Potresti risolvere un problema più difficile del necessario ...
Aha. Penso di averlo capito adesso.
Manca un comodo iPod (o qualsiasi altro tipo, del resto), non so se vuoi esattamente blocchi 4K, nel qual caso IO # read (4000) sarebbe tuo amico (4000 o 4096?) o se sei più felice di rompere la linea, nel qual caso qualcosa del genere dovrebbe funzionare:
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__ == class String
def size_in_bytes
self.unpack("C*").size
end
end
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
Nota l'uso di readline di IO # per ottenere tutto il testo in una bava: farebbe anche #each o #each_line. Ho usato String # chomp! per garantire che qualunque cosa stia facendo il sistema operativo, i byte alla fine vengono rimossi, in modo che \ n o qualunque cosa possa essere forzata nell'output.
Suggerirei di utilizzare File # write, anziché #print o #puts per l'output, poiché questi ultimi hanno la tendenza a fornire sequenze di newline specifiche del sistema operativo.
Se sei davvero preoccupato per i caratteri multi-byte, considera di prendere le opzioni each_byte o unpack (C *) e String patch per scimmia, qualcosa del genere:
<*>La versione di decompressione è circa 8 volte più veloce di quella di each_byte sulla mia macchina, tra l'altro.
Altri suggerimenti
Potresti provare IO # each_byte, ad esempio
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}"
Questo, ovviamente, non ti dà una linea alla volta. La tua migliore opzione è quella di passare attraverso il file tramite each_byte
fino a quando non incontri \ r \ n
. La classe IO offre un sacco di metodi di lettura di basso livello che potrebbero essere utili.
Potresti avere diversi problemi di sovrapposizione qui:
-
Caratteri avanzamento riga
\ r \ n
vs.\ n
(come da post precedente). Anche il carattere del file EOF (^ Z)? -
Definizione di " size " nell'affermazione del problema: intendi "quanti caratteri" (tenendo conto della codifica dei caratteri multi-byte) o vuoi dire "quanti byte"?
-
Interazione della variabile globale
$ KCODE
(deprecata in ruby ??1.9. VediString # codifica
e amici se hai meno di 1.9). Ci sono, ad esempio, caratteri accentati nel tuo file? -
La stringa di formato per
#unpack
. Penso che tu vogliaC *
se vuoi davvero contare i byte.
Nota anche l'esistenza di IO # each_line
(solo così puoi buttare via il mentre
ed essere un po 'più rubino-idiomatico; -)).
Il problema è che quando salvi un file di testo su Windows, le tue interruzioni di riga sono due caratteri (caratteri 13 e 10) e quindi 2 byte, quando lo salvi su Linux ne esiste solo 1 (carattere 10). Tuttavia, ruby ??riporta entrambi questi come un singolo carattere '\ n' - dice il carattere 10. Quel che è peggio, è che se sei su Linux con un file Windows, Ruby ti darà entrambi i caratteri.
Quindi, se sai che i tuoi file provengono sempre da file di testo di Windows ed eseguiti su Windows, ogni volta che ottieni un carattere di nuova riga puoi aggiungere 1 al tuo conteggio. Altrimenti è un paio di condizionali e una piccola macchina statale.
A proposito non c'è un 'personaggio' EOF.
f = File.new("log.txt")
begin
while (line = f.readline)
line.chomp
puts line.length
end
rescue EOFError
f.close
end
Ecco una soluzione semplice, presumendo che il puntatore del file corrente sia impostato all'inizio di una riga nel file letto:
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)
in questo esempio " file " è il file da cui stai leggendo. Per fare questo in un ciclo:
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