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?


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.


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


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


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.


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

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.


6

e quanto fica ?

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

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.