Question

J'écris ce petit HelloWorld à la suite de ceci et les chiffres ne concordent pas

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}"

Le résultat n'est pas identique à la taille du fichier. Je pense que je dois juste savoir quel format je dois me brancher ... ou peut-être ai-je tout manqué. Comment puis-je mesurer la taille du fichier ligne par ligne?

Remarque: je suis sous Windows et le fichier est codé en tant que type ANSI.

Modifier: Cela produit les mêmes résultats!

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}"

donc quiconque peut aider maintenant ...

Était-ce utile?

La solution

IO # gets fonctionne de la même manière que si vous capturiez une entrée à partir de la ligne de commande: le " Entrée " n'est pas envoyé dans le cadre de l'entrée; elle n'est pas non plus transmise lorsque #gets est appelé dans un fichier ou dans une autre sous-classe d'IO, de sorte que les nombres ne vont certainement pas correspondre.

Voir la section de la pioche correspondante

Puis-je savoir pourquoi vous êtes si préoccupé par la longueur des lignes qui correspond à la taille du fichier? Vous résolvez peut-être un problème plus difficile que nécessaire ...

Aha. Je pense que je l’ai maintenant.

En l'absence d'un iPod pratique (ou de tout autre type, d'ailleurs), je ne sais pas si vous voulez exactement des morceaux de 4K, auquel cas IO # read (4000) serait votre ami (4000 ou 4096?) ou si vous êtes plus en mesure de faire la ligne par ligne, dans ce cas, une solution de ce type devrait fonctionner:

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

Notez que l'utilisation des lignes de texte IO # pour obtenir tout le texte dans un slurp: #each ou #each_line conviendrait également. J'ai utilisé String # chomp! pour vous assurer que quoi que fasse le système d'exploitation, les octets à la fin soient supprimés, de sorte que \ n ou quoi que ce soit puisse être forcé dans la sortie.

Je suggérerais d'utiliser Fichier # write plutôt que #print ou #puts pour la sortie, car ces derniers ont tendance à fournir des séquences de nouvelle ligne spécifiques à un système d'exploitation.

Si vous êtes vraiment préoccupé par les caractères multi-octets, envisagez d'utiliser les options each_byte ou unpack (C *) et la chaîne monkey-patcher, comme ceci:

<*>

La version de décompression est environ 8 fois plus rapide que celle de each_byte sur ma machine, d'ailleurs.

Autres conseils

Vous pouvez essayer IO # each_byte, par exemple.

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}"

Cela, bien sûr, ne vous donne pas une ligne à la fois. Votre meilleure option pour cela consiste probablement à parcourir le fichier via chaque_byte jusqu'à ce que vous rencontriez \ r \ n . La classe IO fournit un ensemble de méthodes de lecture assez simples qui pourraient être utiles.

Vous avez potentiellement plusieurs problèmes qui se chevauchent ici:

  1. Caractères de saut de ligne \ r \ n vs. \ n (comme dans votre message précédent). Aussi le caractère de fichier EOF (^ Z)?

  2. Définition de " taille " dans votre énoncé de problème: voulez-vous dire "combien de caractères"? (en tenant compte des codages de caractères multi-octets) ou voulez-vous dire "combien d'octets"?

  3. Interaction de la variable globale $ KCODE (obsolète dans Ruby 1.9. Voir String # encoding et amis si vous utilisez une version inférieure à 1.9). Existe-t-il, par exemple, des caractères accentués dans votre fichier?

  4. Votre chaîne de format pour #unpack . Je pense que vous voulez C * ici si vous voulez vraiment compter les octets.

Notez également l'existence de IO # each_line (afin que vous puissiez jeter le tant que et être un peu plus ruby-idiomatique; -)).

Le problème est que lorsque vous enregistrez un fichier texte sous Windows, vos sauts de ligne sont composés de deux caractères (caractères 13 et 10) et donc de 2 octets. Lorsque vous l’enregistrez sur linux, il n’ya qu’un (caractère 10). Cependant, ruby ??les signale comme un seul caractère '\ n' - il indique le caractère 10. Le pire est que si vous êtes sur Linux avec un fichier Windows, Ruby vous donnera les deux caractères.

Ainsi, si vous savez que vos fichiers proviennent toujours de fichiers texte Windows et sont exécutés sous Windows, vous pouvez ajouter 1 à votre compte à chaque nouvelle ligne. Sinon, il s’agit de deux conditions et d’une petite machine à états.

BTW il n'y a pas de 'caractère' EOF.

f = File.new("log.txt")
begin
    while (line = f.readline)
        line.chomp
        puts line.length
    end
rescue EOFError
    f.close
end

Voici une solution simple, en supposant que le pointeur de fichier actuel est défini au début d'une ligne dans le fichier en lecture:

    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)

dans cet exemple, "fichier". est le fichier à partir duquel vous lisez. Pour le faire en boucle:

    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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top