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 ...

Foi útil?

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:

  1. caracteres de avanço de linha \r\n vs. \n (como por seu post anterior). Também caráter arquivo EOF (^ Z)?

  2. 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"

  3. A interação da variável global $KCODE (reprovado no Ruby 1.9. Veja String#encoding e amigos se você estiver executando sob 1.9). Há, por exemplo, caracteres acentuados no seu arquivo?

  4. A seqüência de formato para #unpack. Eu acho que você quer C* 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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top