Comment lire les lignes d'un fichier en Ruby

238

J'essayais d'utiliser le code suivant pour lire les lignes d'un fichier. Mais lors de la lecture d'un fichier , le contenu est tout sur une seule ligne:

line_num=0
File.open('xxx.txt').each do |line|
  print "#{line_num += 1} #{line}"
end

Mais ce fichier imprime chaque ligne séparément.


Je dois utiliser stdin, comme ruby my_prog.rb < file.txt, où je ne peux pas supposer quel est le caractère de fin de ligne que le fichier utilise. Comment puis-je le gérer?

dessiner
la source
7
Plutôt que de le faire line_num = 0, vous pouvez utiliser each.each_with_indexou éventuellement each.with_index.
Andrew Grimm
@ andrew-grimm merci, ça rend le code plus propre.
tirage
Voir stackoverflow.com/q/25189262/128421 pour savoir pourquoi IO ligne par ligne est préférable à l'utilisation read.
The Tin Man
Utiliser line.chomppour gérer les fins de ligne (gracieuseté de @SreenivasanAC )
Yarin

Réponses:

150

Je crois que ma réponse couvre vos nouvelles préoccupations concernant la gestion de tout type de fins de ligne depuis les deux "\r\n"et "\r"sont converties au standard Linux "\n"avant d'analyser les lignes.

Pour prendre en charge le "\r"caractère EOL avec le standard "\n"et "\r\n"depuis Windows, voici ce que je ferais:

line_num=0
text=File.open('xxx.txt').read
text.gsub!(/\r\n?/, "\n")
text.each_line do |line|
  print "#{line_num += 1} #{line}"
end

Bien sûr, cela pourrait être une mauvaise idée sur les très gros fichiers car cela signifie charger le fichier entier en mémoire.

Olivier L.
la source
Ce regex n'a pas fonctionné pour moi. Le format Unix utilise \ n, Windows \ r \ n, mac utilise \ n - .gsub (/ (\ r | \ n) + /, "\ n") a fonctionné pour moi avec tous les cas.
Pod
4
Une expression régulière correcte devrait /\r?\n/couvrir à la fois \ r \ n et \ n sans combiner les lignes vides comme le ferait le commentaire de Pod
Irongaze.com
12
Cela lira le fichier entier en mémoire, ce qui pourrait être impossible selon la taille du fichier.
eremzeit
1
Cette méthode est très hautement inefficace, les talabes répondent ici stackoverflow.com/a/17415655/228589 est la meilleure réponse. Veuillez vérifier la mise en œuvre de ces deux méthodes.
CantGetANick
1
Ce n'est pas la voie rubis. La réponse ci-dessous montre le bon comportement.
Merovex
525

Ruby a une méthode pour cela:

File.readlines('foo').each do |line|

http://ruby-doc.org/core-1.9.3/IO.html#method-c-readlines

Jonathan
la source
ce methond plus lent que le methond qui est @Olivier L.
HelloWorld
1
@HelloWorld Probablement parce qu'il supprime chaque ligne précédente de la mémoire et se charge dans chaque ligne en mémoire. Peut-être tort, mais Ruby fait probablement les choses correctement (afin que les gros fichiers ne provoquent pas le plantage de votre script).
Starkers
Pouvez-vous également l'utiliser with_indexavec cela?
Joshua Pinter
1
Oui, vous pouvez, par exempleFile.readlines(filename).each_with_index { |line, i| puts "#{i}: #{line}" }
wulftone
Cette méthode semble meilleure. Je lis des fichiers très volumineux et de cette façon, il ne plante pas l'application en essayant de charger le fichier entier en mémoire à la fois.
Shelby S
393
File.foreach(filename).with_index do |line, line_num|
   puts "#{line_num}: #{line}"
end

Cela exécutera le bloc donné pour chaque ligne du fichier sans mettre le fichier entier en mémoire. Voir: IO :: foreach .

talabes
la source
10
Ceci est la réponse - Ruby idiomatique et ne ralentit pas le fichier. Voir aussi stackoverflow.com/a/5546681/165673
Yarin
4
Saluez tous les dieux Ruby!
Joshua Pinter
comment aller à la deuxième ligne à l'intérieur de la boucle?
user1735921
18

Votre premier fichier a des fins de ligne Mac Classic (c'est au "\r"lieu de l'habituel "\n"). Ouvrez-le avec

File.open('foo').each(sep="\r") do |line|

pour spécifier les fins de ligne.

Josh Lee
la source
1
Malheureusement, il n'y a rien de tel que les nouvelles lignes universelles en Python, du moins que je sache.
Josh Lee
encore une question, je dois utiliser stdin, comme ruby ​​my_prog.rb <file.txt, où je ne peux pas supposer ce que le caractère de fin de ligne utilise le fichier ... Comment puis-je le gérer?
tirage
La réponse d'Olivier semble utile, si vous êtes d'accord avec le chargement de tout le fichier en mémoire. Détecter les sauts de ligne tout en analysant le fichier demandera un peu plus de travail.
Josh Lee
7

C'est à cause des extrémités de chaque ligne. Utilisez la méthode chomp dans ruby ​​pour supprimer la ligne de fin '\ n' ou 'r' à la fin.

line_num=0
File.open('xxx.txt').each do |line|
  print "#{line_num += 1} #{line.chomp}"
end
Sreenivasan AC
la source
2
@SreenivisanAC +1 pour chomp!
Yarin
7

Je suis partisan de l'approche suivante pour les fichiers qui ont des en-têtes:

File.open(file, "r") do |fh|
    header = fh.readline
    # Process the header
    while(line = fh.gets) != nil
        #do stuff
    end
end

Cela vous permet de traiter une ou plusieurs lignes d'en-tête différemment des lignes de contenu.

Ron Gejman
la source
6

que diriez-vous obtient ?

myFile=File.open("paths_to_file","r")
while(line=myFile.gets)
 //do stuff with line
end
JBoy
la source
4

N'oubliez pas que si vous êtes préoccupé par la lecture d'un fichier qui pourrait avoir d'énormes lignes qui pourraient submerger votre RAM pendant l'exécution, vous pouvez toujours lire le fichier morceau par repas. Voir " Pourquoi slurper un fichier est mauvais ".

File.open('file_path', 'rb') do |io|
  while chunk = io.read(16 * 1024) do
    something_with_the chunk
    # like stream it across a network
    # or write it to another file:
    # other_io.write chunk
  end
end
Nels
la source