Como ler linhas de um arquivo em Ruby

238

Eu estava tentando usar o código a seguir para ler linhas de um arquivo. Mas ao ler um arquivo , o conteúdo está todo em uma linha:

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

Mas esse arquivo imprime cada linha separadamente.


Eu tenho que usar stdin, tipo ruby my_prog.rb < file.txt, onde eu não posso assumir qual é o caractere de final de linha que o arquivo usa. Como posso lidar com isso?

desenhar
fonte
7
Em vez de fazer line_num = 0, você poderia usar each.each_with_indexou possivelmente each.with_index.
Andrew Grimm
@ andrew-grimm obrigado, torna o código mais limpo.
empate
Consulte stackoverflow.com/q/25189262/128421 para saber por que a IO linha por linha é preferível ao uso read.
the Tin Man
Usar line.chomppara lidar com as terminações de linha (cortesia de @SreenivasanAC )
Yarin

Respostas:

150

Eu acredito que a minha resposta cobre suas novas preocupações sobre a manipulação de qualquer tipo de fins de linha uma vez que ambos "\r\n"e "\r"são convertidos em padrão Linux "\n"antes da análise das linhas.

Para dar suporte ao "\r"caractere EOL junto com o regular "\n"e "\r\n"do Windows, aqui está o que eu faria:

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

Obviamente, isso pode ser uma má idéia para arquivos muito grandes, pois significa carregar o arquivo inteiro na memória.

Olivier L.
fonte
Esse regex não funcionou para mim. O formato Unix usa \ n, windows \ r \ n, mac usa \ n - .gsub (/ (\ r | \ n) + /, "\ n") funcionou para mim em todos os casos.
Pod
4
Regex correto deve ser /\r?\n/o que irá abranger tanto \ r \ n \ n sem combinar linhas vazias como o comentário de Pod faria
Irongaze.com
12
Isso lerá o arquivo inteiro na memória, o que pode ser impossível, dependendo do tamanho do arquivo.
eremzeit
1
Esse método é altamente ineficiente, e a resposta do talabes aqui stackoverflow.com/a/17415655/228589 é a melhor resposta. Por favor, verifique a implementação desses dois métodos.
precisa saber é o seguinte
1
Este não é o caminho do rubi. A resposta abaixo mostra o comportamento certo.
27415 Merovex
525

Ruby tem um método para isso:

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

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

Jonathan
fonte
esse método mais lento que o método que é @Olivier L.
HelloWorld
1
@ HelloWorld Provavelmente porque está excluindo cada linha anterior da memória e carregando cada linha na memória. Pode estar errado, mas Ruby provavelmente está fazendo as coisas corretamente (para que arquivos grandes não causem falha no script).
Starkers,
Você pode usar with_indexcom isso também?
Joshua Pinter
1
Sim, você pode, por exemplo,File.readlines(filename).each_with_index { |line, i| puts "#{i}: #{line}" }
wulftone 17/17
Este método parece melhor. Estou lendo arquivos muito grandes e, dessa forma, ele não trava o aplicativo, tentando carregar o arquivo inteiro na memória de uma só vez.
Shelby S
393
File.foreach(filename).with_index do |line, line_num|
   puts "#{line_num}: #{line}"
end

Isso executará o bloco especificado para cada linha do arquivo sem colocar o arquivo inteiro na memória. Veja: IO :: foreach .

talabes
fonte
10
Esta é a resposta - Ruby idiomático e não inibe o arquivo. Veja também stackoverflow.com/a/5546681/165673
Yarin
4
Todos saudam os deuses Ruby!
Joshua Pinter
como ir para a segunda linha dentro do loop?
user1735921
18

Seu primeiro arquivo tem finais de linha do Mac Classic (isso é o que é "\r"habitual "\n"). Abra com

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

para especificar as terminações da linha.

Josh Lee
fonte
1
Infelizmente, não há nada como as novas linhas universais em Python, pelo menos que eu saiba.
Josh Lee
mais uma pergunta, eu tenho que usar stdin, como ruby ​​my_prog.rb <file.txt, onde não posso assumir qual é a linha final que o arquivo usa ... Como posso lidar com isso?
chamar a
A resposta de Olivier parece útil, se você estiver bem em carregar o arquivo inteiro na memória. Detectar novas linhas enquanto ainda digitaliza o arquivo exige um pouco mais de trabalho.
Josh Lee
7

É por causa das linhas finais em cada linha. Use o método chomp em ruby ​​para excluir a linha final '\ n' ou 'r' no final.

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

Sou parcial com a seguinte abordagem para arquivos com cabeçalhos:

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

Isso permite processar uma linha (ou linhas) de cabeçalho diferente das linhas de conteúdo.

Ron Gejman
fonte
6

e quanto fica ?

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

Não se esqueça de que, se você estiver preocupado com a leitura de um arquivo que possa ter grandes linhas que poderiam inundar sua RAM durante o tempo de execução, sempre poderá ler o arquivo em pedaços. Consulte " Por que compactar um arquivo é ruim ".

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
fonte