Lendo apenas linhas específicas

215

Estou usando um loop for para ler um arquivo, mas só quero ler linhas específicas, digamos as linhas 26 e 30. Existe algum recurso interno para conseguir isso?

obrigado

eozzy
fonte
1
Possível dup: stackoverflow.com/questions/620367/…
Adam Matan

Respostas:

253

Se o arquivo a ser lido for grande e você não quiser ler o arquivo inteiro na memória de uma só vez:

fp = open("file")
for i, line in enumerate(fp):
    if i == 25:
        # 26th line
    elif i == 29:
        # 30th line
    elif i > 29:
        break
fp.close()

Observe que i == n-1para a nlinha th.


No Python 2.6 ou posterior:

with open("file") as fp:
    for i, line in enumerate(fp):
        if i == 25:
            # 26th line
        elif i == 29:
            # 30th line
        elif i > 29:
            break
Alok Singhal
fonte
8
enumerate(x)usa x.next, portanto, não precisa do arquivo inteiro na memória.
Alok Singhal
3
Minha carne pequena com isso é que A) Você deseja usar com o par aberto / fechado e, assim, manter o corpo curto, B) Mas o corpo não é tão curto. Soa como uma troca entre velocidade / espaço e ser pitônico. Não tenho certeza de qual seria a melhor solução.
Hamish Grubijan
5
com é superestimada, python nos dávamos muito bem para mais de 13 anos sem ele
Dan D.
38
@ Dan D. A eletricidade é superestimada, a humanidade se dá bem há mais de 200 mil anos sem ela. ;-) 'with' está tornando-o mais seguro, mais legível e uma linha mais curta.
Romain Vincent
9
por que usar o loop, acho que você não entende o significado de big file. O loop vai levar anos para chegar ao índice
devssh
159

A resposta rápida:

f=open('filename')
lines=f.readlines()
print lines[25]
print lines[29]

ou:

lines=[25, 29]
i=0
f=open('filename')
for line in f:
    if i in lines:
        print i
    i+=1

Existe uma solução mais elegante para extrair muitas linhas: linecache (cortesia de "python: como pular para uma linha específica em um grande arquivo de texto?" , Uma pergunta anterior sobre stackoverflow.com).

Citando a documentação do python vinculada acima:

>>> import linecache
>>> linecache.getline('/etc/passwd', 4)
'sys:x:3:3:sys:/dev:/bin/sh\n'

Mude 4para o número da linha desejada e pronto. Observe que 4 trará a quinta linha, pois a contagem é baseada em zero.

Se o arquivo for muito grande e causar problemas ao ler na memória, pode ser uma boa idéia seguir os conselhos de @ Alok e usar enumerate () .

Concluir:

  • Use fileobject.readlines()ou for line in fileobjectcomo uma solução rápida para arquivos pequenos.
  • Use linecachepara uma solução mais elegante, que será bastante rápida para ler muitos arquivos, possível repetidamente.
  • Siga os conselhos do @ Alok e use-osenumerate() em arquivos que podem ser muito grandes e não cabem na memória. Observe que o uso desse método pode ficar lento porque o arquivo é lido sequencialmente.
Adam Matan
fonte
7
Agradável. Eu apenas olhei para a fonte do linecachemódulo e parece que ele lê o arquivo inteiro na memória. Portanto, se o acesso aleatório é mais importante que a otimização de tamanho, linecacheé o melhor método.
Alok Singhal
7
com linecache.getlin ('some_file', 4) recebo a quarta linha, não a quinta.
Juan
fato interessante: se você usar um conjunto em vez da lista no segundo exemplo, terá O (1) tempo de execução. Procurar em uma lista é O (n). Conjuntos internos são representados como hashes, e é por isso que você obtém o tempo de execução O (1). não é grande coisa neste exemplo, mas se você estiver usando uma grande lista de números e se preocupa com a eficiência, os conjuntos são o caminho a seguir.
Rady
linecacheagora parece funcionar apenas para arquivos de origem python
Paul H #
Você também pode usar linecache.getlines('/etc/passwd')[0:4]para ler as primeira, segunda, terceira e quarta linhas.
Zyy 04/12/19
30

Uma abordagem rápida e compacta pode ser:

def picklines(thefile, whatlines):
  return [x for i, x in enumerate(thefile) if i in whatlines]

isso aceita qualquer objeto aberto semelhante a um arquivo thefile(deixando ao chamador se ele deve ser aberto a partir de um arquivo em disco ou via, por exemplo, um soquete ou outro fluxo semelhante a um arquivo) e um conjunto de índices de linha baseados em zero whatlinese retorna um lista, com pouca presença de memória e velocidade razoável. Se o número de linhas a serem retornadas for grande, você pode preferir um gerador:

def yieldlines(thefile, whatlines):
  return (x for i, x in enumerate(thefile) if i in whatlines)

o que é basicamente bom apenas para repetição - observe que a única diferença vem do uso de parênteses arredondados em vez de quadrados na returndeclaração, fazendo uma compreensão de lista e uma expressão de gerador, respectivamente.

Observe também que, apesar da menção de "linhas" e "arquivo", essas funções são muito, muito mais gerais - elas funcionam em qualquer iterável, seja um arquivo aberto ou outro, retornando uma lista (ou gerador) de itens com base em seus números de itens progressivos. Então, eu sugiro usar nomes gerais mais apropriadamente ;-).

Alex Martelli
fonte
@hememient, eu discordo - o genexp lê de forma suave e perfeita.
Alex Martelli
Solução excelente e elegante, obrigado! De fato, mesmo arquivos grandes devem ser suportados, com a expressão do gerador. Não pode ficar mais elegante que isso, pode? :)
Samuel Lampa
Solução agradável, como isso se compara ao proposto por @AdamMatan? A solução Adam pode ser mais rápida, pois explora informações adicionais (o número de linhas aumenta monotonicamente), o que pode levar a uma parada antecipada. Eu tenho um arquivo de 10 GB que não consigo carregar na memória.
precisa
2
@Annaggia Não é enfatizado o suficiente nesta resposta, mas whatlinesdeve ser um set, porque if i in whatlinesserá executado mais rapidamente com um conjunto do que com uma lista (classificada). Eu não percebi isso primeiro e, em vez disso, criei minha própria solução feia com lista classificada (onde eu não precisava varrer uma lista todas as vezes, enquanto if i in whatlinesfaz exatamente isso), mas a diferença no desempenho era insignificante (com meus dados) e isso solução é muito mais elegante.
21715 Victor K
28

Para oferecer outra solução:

import linecache
linecache.getline('Sample.txt', Number_of_Line)

Espero que seja rápido e fácil :)

KingMak
fonte
1
Espero que seja a melhor solução.
Maniac_user 14/10
2
Isso lê o arquivo inteiro na memória. Você pode muito bem chamar file.read () split ( '\ n'), em seguida, usar pesquisas de índice de matriz para obter a linha de interesse ....
duhaime
Você poderia fornecer um exemplo @duhaime
anon
14

se você quer a linha 7

line = open ("arquivo.txt", "r"). readlines () [7]
MadSc13ntist
fonte
14
Arrumado. Mas como você close()abre o arquivo dessa maneira?
Milo Wielondek
1
@ 0sh precisamos fechar?
Ooker
1
sim. precisamos fechar depois disso. Quando abrimos um arquivo usando "with" ... ele se fecha.
precisa saber é o seguinte
10

Por uma questão de integridade, aqui está mais uma opção.

Vamos começar com uma definição dos documentos python :

fatia Um objeto geralmente contendo uma parte de uma sequência. Uma fatia é criada usando a notação subscrita, [] com dois pontos entre os números quando vários são fornecidos, como na variável_name [1: 3: 5]. A notação de colchete (subscrito) usa objetos de fatia internamente (ou em versões mais antigas, __getslice __ () e __setslice __ ()).

Embora a notação de fatia não seja diretamente aplicável aos iteradores em geral, o itertoolspacote contém uma função de substituição:

from itertools import islice

# print the 100th line
with open('the_file') as lines:
    for line in islice(lines, 99, 100):
        print line

# print each third line until 100
with open('the_file') as lines:
    for line in islice(lines, 0, 100, 3):
        print line

A vantagem adicional da função é que ela não lê o iterador até o final. Então você pode fazer coisas mais complexas:

with open('the_file') as lines:
    # print the first 100 lines
    for line in islice(lines, 100):
        print line

    # then skip the next 5
    for line in islice(lines, 5):
        pass

    # print the rest
    for line in lines:
        print line

E para responder à pergunta original:

# how to read lines #26 and #30
In [365]: list(islice(xrange(1,100), 25, 30, 4))
Out[365]: [26, 30]
newtover
fonte
1
De longe, a melhor abordagem ao trabalhar com arquivos grandes. Meu programa passou de consumir 8 GB + a quase nada. O problema foi o uso da CPU, que passou de ~ 15% a ~ 40%, mas o processamento real do arquivo foi 70% mais rápido. Vou levar isso para o dia todo. Te agradece! Gol
GollyJer 26/09/18
1
Isso me parece mais pitônico. Obrigado!
Ipetrik
10

A leitura de arquivos é incrivelmente rápida. A leitura de um arquivo de 100 MB leva menos de 0,1 segundos (consulte meu artigo Lendo e gravando arquivos com Python ). Portanto, você deve lê-lo completamente e depois trabalhar com as linhas únicas.

O que a maioria das respostas aqui faz não é errado, mas tem um estilo ruim. A abertura de arquivos sempre deve ser feita with, pois garante que o arquivo seja fechado novamente.

Então você deve fazer assim:

with open("path/to/file.txt") as f:
    lines = f.readlines()
print(lines[26])  # or whatever you want to do with this line
print(lines[30])  # or whatever you want to do with this line

Arquivos enormes

Se houver um grande arquivo e o consumo de memória for uma preocupação, você poderá processá-lo linha por linha:

with open("path/to/file.txt") as f:
    for i, line in enumerate(f):
        pass  # process line i
Martin Thoma
fonte
Na IMO, é um estilo muito ruim ler um arquivo inteiro de comprimento desconhecido, apenas para obter as primeiras 30 linhas ... o que é o consumo de memória e o que são os fluxos intermináveis?
precisa saber é o seguinte
@ return42 Depende muito da aplicação. Para muitos, é totalmente bom supor que um arquivo de texto tenha um tamanho muito menor que a memória disponível. Se houver arquivos potencialmente enormes, editei minha resposta.
Martin Thoma
obrigado por seu lado, que é o mesmo que alok resposta . E desculpe não, acho que isso não depende do aplicativo. OMI é sempre melhor não ler mais linhas do que você precisa.
precisa saber é o seguinte
7

Alguns deles são adoráveis, mas isso pode ser feito de maneira muito mais simples:

start = 0 # some starting index
end = 5000 # some ending index
filename = 'test.txt' # some file we want to use

with open(filename) as fh:
    data = fin.readlines()[start:end]

print(data)

Isso usará simplesmente o faturamento de lista, carregará o arquivo inteiro, mas a maioria dos sistemas minimizará o uso de memória adequadamente, é mais rápido que a maioria dos métodos fornecidos acima e funciona nos meus arquivos de dados 10G +. Boa sorte!

Vai
fonte
4

Você pode fazer uma chamada seek () que posiciona sua cabeça de leitura em um byte especificado no arquivo. Isso não ajudará você, a menos que você saiba exatamente quantos bytes (caracteres) estão escritos no arquivo antes da linha que deseja ler. Talvez o seu arquivo esteja formatado estritamente (cada linha tem um número X de bytes?) Ou você pode contar o número de caracteres (lembre-se de incluir caracteres invisíveis, como quebras de linha), se quiser realmente aumentar a velocidade.

Caso contrário, você precisará ler todas as linhas anteriores à linha desejada, conforme uma das muitas soluções já propostas aqui.

romano
fonte
3

Se o seu arquivo de texto grande fileestiver estritamente bem estruturado (o que significa que cada linha tem o mesmo comprimento l), você poderá usar a n-a linha

with open(file) as f:
    f.seek(n*l)
    line = f.readline() 
    last_pos = f.tell()

Isenção de responsabilidade Isso funciona apenas para arquivos com o mesmo comprimento!

Michael Dorner
fonte
2

Que tal agora:

>>> with open('a', 'r') as fin: lines = fin.readlines()
>>> for i, line in enumerate(lines):
      if i > 30: break
      if i == 26: dox()
      if i == 30: doy()
Hamish Grubijan
fonte
É verdade, isso é menos eficiente do que o de Alok, mas meu usa uma com a declaração;)
Hamish Grubijan
2

Se você não se importa em importar, o fileinput faz exatamente o que você precisa (você pode ler o número da linha atual)

ennuikiller
fonte
2
def getitems(iterable, items):
  items = list(items) # get a list from any iterable and make our own copy
                      # since we modify it
  if items:
    items.sort()
    for n, v in enumerate(iterable):
      if n == items[0]:
        yield v
        items.pop(0)
        if not items:
          break

print list(getitems(open("/usr/share/dict/words"), [25, 29]))
# ['Abelson\n', 'Abernathy\n']
# note that index 25 is the 26th item

fonte
Roger, meu cara favorito! Isso pode se beneficiar de uma declaração with.
Hamish Grubijan
2

Prefiro essa abordagem porque é de uso geral, ou seja, você pode usá-la em um arquivo, no resultado de f.readlines(), em um StringIOobjeto, o que for:

def read_specific_lines(file, lines_to_read):
   """file is any iterable; lines_to_read is an iterable containing int values"""
   lines = set(lines_to_read)
   last = max(lines)
   for n, line in enumerate(file):
      if n + 1 in lines:
          yield line
      if n + 1 > last:
          return

>>> with open(r'c:\temp\words.txt') as f:
        [s for s in read_specific_lines(f, [1, 2, 3, 1000])]
['A\n', 'a\n', 'aa\n', 'accordant\n']
Robert Rossney
fonte
2

Aqui estão meus pequenos 2 centavos, pelo que vale a pena;)

def indexLines(filename, lines=[2,4,6,8,10,12,3,5,7,1]):
    fp   = open(filename, "r")
    src  = fp.readlines()
    data = [(index, line) for index, line in enumerate(src) if index in lines]
    fp.close()
    return data


# Usage below
filename = "C:\\Your\\Path\\And\\Filename.txt"
for line in indexLines(filename): # using default list, specify your own list of lines otherwise
    print "Line: %s\nData: %s\n" % (line[0], line[1])
AWainb
fonte
2

Uma mudança melhor e menor para a resposta de Alok Singhal

fp = open("file")
for i, line in enumerate(fp,1):
    if i == 26:
        # 26th line
    elif i == 30:
        # 30th line
    elif i > 30:
        break
fp.close()
sedic
fonte
1

@OP, você pode usar enumerar

for n,line in enumerate(open("file")):
    if n+1 in [26,30]: # or n in [25,29] 
       print line.rstrip()
ghostdog74
fonte
1
file = '/path/to/file_to_be_read.txt'
with open(file) as f:
    print f.readlines()[26]
    print f.readlines()[30]

Usando a instrução with, isso abre o arquivo, imprime as linhas 26 e 30 e depois fecha o arquivo. Simples!

user3901273
fonte
Esta não é uma resposta válida. após a primeira chamada para readlines()o iterador será esgotado e a segunda chamada será ou retornar uma lista vazia ou lançar um erro (não lembro qual)
Paul H
1

Você pode fazer isso de maneira muito simples com esta sintaxe que alguém já mencionou, mas é de longe a maneira mais fácil de fazer isso:

inputFile = open("lineNumbers.txt", "r")
lines = inputFile.readlines()
print (lines[0])
print (lines[2])
Trey50Daniel
fonte
1

Para imprimir a linha 3,

line_number = 3

with open(filename,"r") as file:
current_line = 1
for line in file:
    if current_line == line_number:
        print(file.readline())
        break
    current_line += 1

Autor original: Frank Hofmann

crazy_daffodils
fonte
1

Bastante rápido e direto ao ponto.

Para imprimir certas linhas em um arquivo de texto. Crie uma lista "lines2print" e imprima apenas quando a enumeração estiver "na" lista de lines2print. Para se livrar de '\ n' extra, use line.strip () ou line.strip ('\ n'). Eu apenas gosto de "compreensão da lista" e tento usar quando posso. Eu gosto do método "with" para ler arquivos de texto, a fim de evitar que um arquivo seja aberto por qualquer motivo.

lines2print = [26,30] # can be a big list and order doesn't matter.

with open("filepath", 'r') as fp:
    [print(x.strip()) for ei,x in enumerate(fp) if ei in lines2print]

ou se a lista for pequena, basta digitar a lista como uma lista para a compreensão.

with open("filepath", 'r') as fp:
    [print(x.strip()) for ei,x in enumerate(fp) if ei in [26,30]]
Mike Adrion
fonte
0

Para imprimir a linha desejada. Para imprimir a linha acima / abaixo da linha necessária.

def dline(file,no,add_sub=0):
    tf=open(file)
    for sno,line in enumerate(tf):
        if sno==no-1+add_sub:
         print(line)
    tf.close()

execute ----> dline ("D: \ dummy.txt", 6) ou seja, dline ("caminho do arquivo", número da linha, se você quiser a linha superior da linha pesquisada, forneça 1 para menor -1, este é o valor padrão opcional ser tomado 0)

sudhir tataraju
fonte
0

Se você quiser ler linhas específicas, como a linha iniciada após alguma linha de limite, use os seguintes códigos, file = open("files.txt","r") lines = file.readlines() ## convert to list of lines datas = lines[11:] ## raed the specific lines

Niharranjan Pradhan
fonte
-1
f = open(filename, 'r')
totalLines = len(f.readlines())
f.close()
f = open(filename, 'r')

lineno = 1
while lineno < totalLines:
    line = f.readline()

    if lineno == 26:
        doLine26Commmand(line)

    elif lineno == 30:
        doLine30Commmand(line)

    lineno += 1
f.close()
inspectorG4dget
fonte
7
isso é tão impiedoso quanto possível.
SilentGhost
Dá o resultado errado, pois você não pode usar linhas de leitura e linhas de leitura assim (cada uma muda a posição atual de leitura).
Sinto muito por ter esquecido um erro enorme no meu primeiro código. O erro foi corrigido e o código atual deve funcionar conforme o esperado. Obrigado por apontar meu erro, Roger Pate.
inspectorG4dget
-1

Eu acho que isso funcionaria

 open_file1 = open("E:\\test.txt",'r')
 read_it1 = open_file1.read()
 myline1 = []
 for line1 in read_it1.splitlines():
 myline1.append(line1)
 print myline1[0]
San k
fonte
Já havia uma dúzia de métodos readline quando você postou isso - adicionando outro só aumenta a confusão
duhaime