Quels sont les moyens courants de lire un fichier en Ruby?
Par exemple, voici une méthode:
fileObj = File.new($fileName, "r")
while (line = fileObj.gets)
puts(line)
end
fileObj.close
Je sais que Ruby est extrêmement flexible. Quels sont les avantages/inconvénients de chaque approche?
File.open("my/file/path", "r") do |f|
f.each_line do |line|
puts line
end
end
# File is closed automatically at end of block
Il est également possible de fermer explicitement le fichier après comme ci-dessus (passer un bloc à open
le ferme pour vous):
f = File.open("my/file/path", "r")
f.each_line do |line|
puts line
end
f.close
Le moyen le plus simple si le fichier n'est pas trop long est:
puts File.read(file_name)
En effet, IO.read
ou File.read
ferme automatiquement le fichier, il n'est donc pas nécessaire d'utiliser File.open
avec un bloc.
Méfiez-vous des fichiers "glissants". C'est à ce moment-là que vous lisez tout le fichier en mémoire en une fois.
Le problème est que cela ne va pas bien. Vous pourriez développer du code avec un fichier de taille raisonnable, puis le mettre en production et trouver soudainement que vous essayez de lire des fichiers mesurant en gigaoctets et que votre hôte se bloque alors qu'il tente de lire et d'allouer de la mémoire.
Les E/S ligne par ligne sont très rapides et presque toujours aussi efficaces que le slurping. C'est étonnamment rapide en fait.
J'aime utiliser:
IO.foreach("testfile") {|x| print "GOT ", x }
ou
File.foreach('testfile') {|x| print "GOT", x }
Le fichier hérite d'IO et foreach
est dans IO, vous pouvez donc utiliser l'un ou l'autre.
J'ai quelques points de repère montrant l'impact d'essayer de lire de gros fichiers via read
contre des entrées/sorties ligne par ligne à " Pourquoi" extraire "un fichier n'est pas une bonne pratique? ".
Vous pouvez lire le fichier en une fois:
content = File.readlines 'file.txt'
content.each_with_index{|line, i| puts "#{i+1}: #{line}"}
Lorsque le fichier est volumineux, ou peut être volumineux, il est généralement préférable de le traiter ligne par ligne:
File.foreach( 'file.txt' ) do |line|
puts line
end
Parfois, vous souhaitez cependant accéder au descripteur de fichier ou contrôler vous-même les lectures:
File.open( 'file.txt' ) do |f|
loop do
break if not line = f.gets
puts "#{f.lineno}: #{line}"
end
end
Dans le cas de fichiers binaires, vous pouvez spécifier un séparateur nul et une taille de bloc, comme suit:
File.open('file.bin', 'rb') do |f|
loop do
break if not buf = f.gets(nil, 80)
puts buf.unpack('H*')
end
end
Enfin, vous pouvez le faire sans blocage, par exemple lors du traitement simultané de plusieurs fichiers. Dans ce cas, le fichier doit être explicitement fermé (amélioré conformément au commentaire de @antinome):
begin
f = File.open 'file.txt'
while line = f.gets
puts line
end
ensure
f.close
end
Références: API de fichier et l'API IO .
Une méthode simple consiste à utiliser readlines
:
my_array = IO.readlines('filename.txt')
Chaque ligne du fichier d’entrée sera une entrée du tableau. La méthode gère l’ouverture et la fermeture du fichier pour vous.
file_content = File.read('filename with extension');
puts file_content;
Je fais habituellement ceci:
open(path_in_string, &:read)
Cela vous donnera le texte entier en tant qu'objet chaîne. Cela ne fonctionne que sous Ruby 1.9.
retourne les dernières n lignes de your_file.log ou .txt
path = File.join(Rails.root, 'your_folder','your_file.log')
last_100_lines = `tail -n 100 #{path}`
content = `cat file`
Je pense que cette méthode est la plus "rare". C'est peut-être un peu délicat, mais cela fonctionne si cat
est installé.
Une méthode encore plus efficace consiste à diffuser en continu en demandant au noyau du système d’exploitation d’ouvrir un fichier, puis de lire les octets bit par bit. Lors de la lecture d’un fichier par ligne dans Ruby, les données sont extraites du fichier 512 octets à la fois et sont ensuite divisées en «lignes».
En mettant en mémoire tampon le contenu du fichier, le nombre d’appels d’E/S est réduit lors de la division du fichier en blocs logiques.
Exemple:
Ajoutez cette classe à votre application en tant qu'objet de service:
class MyIO
def initialize(filename)
fd = IO.sysopen(filename)
@io = IO.new(fd)
@buffer = ""
end
def each(&block)
@buffer << @io.sysread(512) until @buffer.include?($/)
line, @buffer = @buffer.split($/, 2)
block.call(line)
each(&block)
rescue EOFError
@io.close
end
end
Appelez-le et transmettez à la méthode :each
un bloc:
filename = './somewhere/large-file-4gb.txt'
MyIO.new(filename).each{|x| puts x }
Lisez à ce sujet ici dans cet article détaillé: