Pergunta

Em Python, Acabo de ler uma linha de formar um arquivo de texto e eu gostaria de saber como código para ignorar os comentários com um hash # no início da linha.

Eu acho que deveria ser algo como isto:

for 
   if line !contain #
      then ...process line
   else end for loop 

Mas eu sou novo em Python e eu não sei a sintaxe

Foi útil?

Solução

Você pode usar startswith ()

ex

for line in open("file"):
    li=line.strip()
    if not li.startswith("#"):
        print line.rstrip()

Outras dicas

Eu recomendo que você não ignore toda a linha quando você vê um personagem #; simplesmente ignorar o resto da linha. Você pode fazer isso facilmente com uma função método string chamada partition:

with open("filename") as f:
    for line in f:
        line = line.partition('#')[0]
        line = line.rstrip()
        # ... do something with line ...

partition retorna uma tupla: tudo antes de a corda partição, a seqüência de partição, e tudo após a seqüência de partição. Então, por indexação com [0] tomarmos apenas a parte antes da seqüência partição.

EDIT: Se você estiver usando uma versão do Python que não tem partition(), aqui é o código que você poderia usar:

with open("filename") as f:
    for line in f:
        line = line.split('#', 1)[0]
        line = line.rstrip()
        # ... do something with line ...

Isto divide a string em um caractere '#', em seguida, mantém tudo antes do desdobramento. O argumento 1 faz a parada método .split() depois de uma separação; uma vez que estamos apenas pegar o substring 0 (por indexação com [0]) que se obtém a mesma resposta sem o argumento 1, mas isso pode ser um pouco mais rápido. (Simplificado de meus agradecimentos código originais para um comentário de @gnr meu código original foi mais confusa sem uma boa razão;.. Graças, @gnr)

Você também pode apenas escrever sua própria versão de partition(). Aqui é um chamado part():

def part(s, s_part):
    i0 = s.find(s_part)
    i1 = i0 + len(s_part)
    return (s[:i0], s[i0:i1], s[i1:])

@dalle observou que '#' pode aparecer dentro de uma string. Não é assim tão fácil de lidar com este caso corretamente, então eu só ignorou, mas eu deveria ter dito alguma coisa.

Se o seu arquivo de entrada tem regras bastante simples para cadeias entre aspas, isso não é difícil. Seria difícil se você aceitou qualquer Python legal citado corda, porque há um único citado, entre aspas, citações de várias linhas com uma barra invertida escapar da linha de fim-de-, cordas citadas triplos (usando aspas simples ou duplas), e cordas até mesmo cru! A única forma possível de lidar corretamente com tudo o que seria uma máquina de estado complicado.

Mas, se nos limitarmos a apenas um simples string citado, podemos segurá-lo com uma máquina de estado simples. Podemos até mesmo permitir que uma dupla citação citou-barra invertida dentro da string.

c_backslash = '\\'
c_dquote = '"'
c_comment = '#'


def chop_comment(line):
    # a little state machine with two state varaibles:
    in_quote = False  # whether we are in a quoted string right now
    backslash_escape = False  # true if we just saw a backslash

    for i, ch in enumerate(line):
        if not in_quote and ch == c_comment:
            # not in a quote, saw a '#', it's a comment.  Chop it and return!
            return line[:i]
        elif backslash_escape:
            # we must have just seen a backslash; reset that flag and continue
            backslash_escape = False
        elif in_quote and ch == c_backslash:
            # we are in a quote and we see a backslash; escape next char
            backslash_escape = True
        elif ch == c_dquote:
            in_quote = not in_quote

    return line

Eu realmente não quero começar este complicado em uma pergunta com a tag "iniciante", mas esta máquina de estado é razoavelmente simples, e eu espero que seja interessante.

Estou chegando neste tarde, mas o problema de lidar com comentários # estilo shell (ou estilo python) é muito comum.

Eu estive usando algum código quase toda vez que eu ler um arquivo de texto.
O problema é que ele não controla comentários citados ou escaparam corretamente . Mas funciona para casos simples e é fácil.

for line in whatever:
    line = line.split('#',1)[0].strip()
    if not line:
        continue
    # process line

A solução mais robusta é usar shlex :

import shlex
for line in instream:
    lex = shlex.shlex(line)
    lex.whitespace = '' # if you want to strip newlines, use '\n'
    line = ''.join(list(lex))
    if not line:
        continue
    # process decommented line

Esta abordagem shlex não manuseia apenas citações e escapa corretamente, ele adiciona um monte de funcionalidade legal (como a capacidade de ter origem arquivos outros arquivos se você quiser). Eu não testei para a velocidade em arquivos grandes, mas é o suficiente zippy de coisas pequenas.

O caso comum quando você também está dividindo cada linha de entrada em campos (no espaço em branco) é ainda mais simples:

import shlex
for line in instream:
    fields = shlex.split(line, comments=True)
    if not fields:
        continue
    # process list of fields 

Esta é a menor forma possível:

for line in open(filename):
  if line.startswith('#'):
    continue
  # PROCESS LINE HERE

O método startswith() em uma corda retorna true se a seqüência que você chamá-lo em começa com a seqüência que você passado.

Enquanto isso é bom em algumas circunstâncias, como shell scripts, ele tem dois problemas. Primeiro, ele não especifica como abrir o arquivo. O modo padrão para abrir um arquivo é 'r', que significa 'ler o arquivo em modo binário'. Desde que você está esperando um arquivo de texto, é melhor abri-lo com 'rt'. Embora esta distinção é irrelevante no UNIX-like sistemas operacionais, é importante no Windows (e em pré-OS X Macs).

O segundo problema é o identificador de arquivo aberto. A função open() retorna um objeto de arquivo, e é considerada uma boa prática para fechar arquivos quando você está feito com eles. Para fazer isso, chame o método close() no objeto. Agora, Python irá provavelmente fazer isso por você, , eventualmente; em Python objetos são contados-referência, e quando a contagem de referência de um objeto vai a zero ele é libertado, e em algum ponto depois que um objeto é liberado Python vai chamar seu destruidor (um método chamado __del__ especial). Note que eu disse provavelmente: Python tem um mau hábito de não realmente chamando o destruidor em objetos cuja contagem de referência cai para zero pouco antes de o programa terminar. Eu acho que é com pressa!

Para os programas de curta duração como shell scripts, e em particular para os objetos de arquivo, isso não importa. Seu sistema operacional irá automaticamente limpar quaisquer identificadores de arquivo deixado em aberto quando o programa terminar. Mas se você abriu o arquivo, ler o conteúdo, em seguida, começou um longo computação sem fechar explicitamente o identificador de arquivo em primeiro lugar, Python é provável que deixar o identificador aberto arquivo durante a sua computação. E isso é má prática.

Esta versão irá funcionar em qualquer versão 2.x do Python, e correções de ambos os problemas que discutimos acima:

f = open(file, 'rt')
for line in f:
  if line.startswith('#'):
    continue
  # PROCESS LINE HERE
f.close()

Esta é a melhor forma geral para versões mais antigas do Python.

Como sugerido por steveha, usando a melhor prática "com" declaração é agora considerado. Se você estiver usando 2.6 ou acima, você deve escrevê-lo desta maneira:

with open(filename, 'rt') as f:
  for line in f:
    if line.startswith('#'):
      continue
    # PROCESS LINE HERE

A declaração "with" irá limpar o identificador de arquivo para você.

Na sua pergunta você disse "linhas que começam com #", então isso é o que eu mostrei aqui. Se você deseja filtrar as linhas que começam com opcional espaços em branco e então um '#', você deve tirar o espaço em branco antes de olhar para o '#'. Nesse caso, você deve alterar esta:

    if line.startswith('#'):

a esta:

    if line.lstrip().startswith('#'):

Em Python, strings são imutáveis, então isso não muda o valor de line. O método lstrip() Retorna uma cópia da string com todos os seus espaços em branco que conduz removido.

Eu descobri recentemente que uma função de gerador faz um ótimo trabalho desta. Eu usei funções similares para pular linhas de comentário, linhas em branco, etc.

Eu defino a minha função como

def skip_comments(file):
    for line in file:
        if not line.strip().startswith('#'):
            yield line

Dessa forma, posso apenas fazer

f = open('testfile')
for line in skip_comments(f):
    print line

Esta é reutilizáveis ??em todo o meu código, e eu posso adicionar qualquer manipulação / logging / etc adicional. que eu preciso.

Eu sei que esta é uma discussão antiga, mas esta é uma função de gerador que eu uso para meus próprios propósitos. Ele retira comentários não importa onde eles aparecer na linha, bem como a decapagem levando / espaços em branco e linhas em branco. O seguinte texto fonte:

# Comment line 1
# Comment line 2

# host01  # This host commented out.
host02  # This host not commented out.
host03
  host04  # Oops! Included leading whitespace in error!

renderá:

host02
host03
host04

código Aqui está documentado, que inclui uma demonstração:

def strip_comments(item, *, token='#'):
    """Generator. Strips comments and whitespace from input lines.

    This generator strips comments, leading/trailing whitespace, and
    blank lines from its input.

    Arguments:
        item (obj):  Object to strip comments from.
        token (str, optional):  Comment delimiter.  Defaults to ``#``.

    Yields:
        str:  Next uncommented non-blank line from ``item`` with
            comments and leading/trailing whitespace stripped.

    """

    for line in item:
        s = line.split(token, 1)[0].strip()
        if s:
            yield s


if __name__ == '__main__':
    HOSTS = """# Comment line 1
    # Comment line 2

    # host01  # This host commented out.
    host02  # This host not commented out.
    host03
      host04  # Oops! Included leading whitespace in error!""".split('\n')


    hosts = strip_comments(HOSTS)
    print('\n'.join(h for h in hosts))

O caso de uso normal, será para retirar os comentários de um arquivo (ou seja, um arquivo hosts, como no meu exemplo acima). Se este for o caso, então o fim da cauda do código acima seria modificado para:

if __name__ == '__main__':
    with open('hosts.txt', 'r') as f:
        hosts = strip_comments(f)

    for host in hosts:
        print('\'%s\'' % host)

A versão mais compacta de uma expressão de filtragem também pode ter esta aparência:

for line in (l for l in open(filename) if not l.startswith('#')):
    # do something with line

(l for ... ) é chamado de "gerador de expressão" que atua aqui como um iterador embrulho que irá filtrar todas as linhas desnecessárias do arquivo enquanto Iterando sobre ele. Não confundi-lo com a mesma coisa em brakets quadrado [l for ... ] que é uma "lista de compreensão" que primeiro ler todas as linhas do arquivo na memória e só então começará a iteração sobre ele.

Às vezes você pode querer tê-lo menos de um Liney e mais legível:

lines = open(filename)
lines = (l for l in lines if ... )
# more filters and mappings you might want
for line in lines:
    # do something with line

Todos os filtros serão executados na mosca em uma iteração.

Use regex re.compile("^(?:\s+)*#|(?:\s+)") para ignorar as novas linhas e comentários.

I tendem a usar

for line  in lines:
    if '#' not in line:
        #do something

Este irá ignorar toda a linha, embora a resposta que inclui rpartition tem o meu upvote, pois ele pode incluir qualquer informação de antes da #

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top