Pregunta

En Python, acabo de leer una línea de formar un archivo de texto y me gustaría saber cómo codificar a ignorar los comentarios con un hash # al principio de la línea.

Creo que debería ser algo como esto:

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

Pero yo soy nuevo en Python y no sé la sintaxis

¿Fue útil?

Solución

startswith ()

por ejemplo

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

Otros consejos

Te recomiendo que no ignore toda la línea cuando se ve a un personaje #; simplemente ignorar el resto de la línea. Puede hacerlo fácilmente con una función llamada método de cadena partition:

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

partition devuelve una tupla: todo antes de la cadena de partición, la cadena de partición, y todo después de la cadena de partición. Así, mediante la indexación con [0] tomamos sólo la parte antes de la cadena partición.

EDIT: Si está utilizando una versión de Python que no tiene partition(), aquí es el código que puede usar:

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

Esto divide la cadena en un carácter '#', a continuación, mantiene todo antes de la división. El argumento 1 hace que el método de parada después de un .split() una fracción; ya que estamos sólo sacar la subcadena 0 ª (indexando con [0]) se obtendría la misma respuesta sin el argumento 1, pero esto podría ser un poco más rápido. (Simplificado de mi código original gracias a un comentario de @gnr Mi código original era más desordenado sin una buena razón;.. Gracias, @gnr)

También puedes, simplemente escribir su propia versión de partition(). Aquí está uno llamado 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 señaló que '#' puede aparecer dentro de una cadena. No es tan fácil de manejar este caso correctamente, así que simplemente ignorado, pero debería haber dicho algo.

Si el archivo de entrada tiene reglas bastante simples para cadenas entre comillas, esto no es difícil. Sería difícil si usted aceptó cualquier cadena legal Python citado, porque hay un solo citar, entre comillas dobles, cotizaciones de varias líneas con una barra invertida de escape de la línea de fin de, cadenas entre comillas triples (con comillas simples o dobles), y cuerdas, incluso crudo! La única forma posible de manejar correctamente todo lo que sería una máquina de estados complicado.

Pero si nos limitamos a una simple cadena entre comillas, podemos manejarlo con una máquina de estados simple. Incluso podemos permitir que una comilla doble barra invertida-cotizado dentro de la cadena.

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

Yo realmente no quiero entrar en este complicado una pregunta etiquetado como "principiante" pero esta máquina de estados es razonablemente simple, y espero que será interesante.

Voy a estas alturas, pero el problema del manejo de los shells de # (o estilo pitón) comenta es muy común.

He estado usando un código casi cada vez que leo un archivo de texto.
El problema es que no maneja comentarios citados o escapados correctamente . Pero funciona para casos simples y es fácil.

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

una solución más robusta es utilizar 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

Este enfoque shlex no sólo se ocupa de las cotizaciones y se escapa correctamente, se añade un montón de funcionalidades fresco (como la posibilidad de tener una fuente archivos otros archivos si lo desea). Yo no lo he probado para la velocidad de archivos de gran tamaño, pero es lo suficientemente enérgico de cosas pequeñas.

El caso común cuando también está dividiendo cada línea de entrada en los campos (el espacio en blanco) es aún más simple:

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

Esta es la forma más breve posible:

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

El método startswith() en una cadena devuelve True si la cadena que lo llaman en los arranques con la cadena que se ha pasado.

Mientras que esto está bien, en algunas circunstancias, como scripts de shell, tiene dos problemas. En primer lugar, no especifica cómo abrir el archivo. El modo por defecto para abrir un archivo es 'r', que significa "leer el archivo en modo binario. Puesto que usted está esperando un archivo de texto que es mejor abrirlo con 'rt'. Aunque esta distinción es irrelevante en los sistemas operativos UNIX, que es importante en Windows (y en la pre-OS X Mac).

El segundo problema es el identificador de archivo abierto. La función open() devuelve un objeto de archivo, y se considera una buena práctica para cerrar archivos cuando haya terminado con ellos. Para ello, llame al método close() en el objeto. Ahora, Python probablemente hacer esto para usted, con el tiempo; en objetos de Python son de referencia contado, y cuando el contador de referencia de un objeto tiende a cero se pone en libertad, y en algún punto después de un objeto es liberado Python llamará a su destructor (un método llamado __del__ especial). Tenga en cuenta que he dicho probablemente: Python tiene un mal hábito de no activar realmente el destructor de objetos cuyo recuento de referencias llega a cero, poco antes de que termine el programa. Supongo que es a toda prisa!

Para los programas de corta duración como scripts de shell, y en particular para los objetos de archivo, esto no importa. Su sistema operativo va a limpiar automáticamente cualquier archivo asas izquierda abierta cuando el programa termina. Pero si se abre el archivo, leer el contenido, y luego comenzó un largo cálculo sin cerrar explícitamente el identificador de archivo en primer lugar, Python es probable que deje el identificador de archivo abierto durante su cálculo. Y eso es una mala práctica.

Esta versión funcionará en cualquier versión 2.x de Python, y correcciones de ambos los problemas que se discutió anteriormente:

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

Esta es la mejor forma general para versiones anteriores de Python.

Según lo sugerido por steveha, utilizando la mejor práctica "por" declaración es ahora considerado. Si estás utilizando 2.6 o superior debe escribir de esta manera:

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

La expresión "with" va a limpiar el identificador de archivo para usted.

En su pregunta usted dijo "líneas que comienzan con" #, así que eso es lo que te he mostrado aquí. Si desea que se corten las líneas que comienzan con espacio en blanco opcional y después a '#', se debe quitar el espacio en blanco antes de buscar el '#'. En ese caso, debe cambiar esto:

    if line.startswith('#'):

a esto:

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

En Python, las cadenas son inmutables, así que esto no cambia el valor de line. El método lstrip() devuelve una copia de la cadena con toda su líder en el espacio en blanco eliminado.

He descubierto recientemente que una función de generador hace un gran trabajo de esta. He usado funciones similares a saltar las líneas de comentarios, líneas en blanco, etc.

defino mi función como

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

De este modo, sólo puede hacerlo

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

Este es reutilizable a través de todo mi código, y puede añadir cualquier manipulación adicional / registro / etc. que necesito.

Sé que este es un viejo hilo, pero esto es una función de generador que me usar para mis propios fines. Se despoja a los comentarios sin importar dónde se aparecerá en la línea, así como de extracción que conduce / espacios en blanco y líneas en blanco. El siguiente texto de origen:

# 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!

rendirá:

host02
host03
host04

Código Aquí está documentado, que incluye una demo:

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))

El caso de uso normal será para despojar a los comentarios de un archivo (es decir, un archivo de hosts, como en el ejemplo anterior). Si este es el caso, entonces el extremo de cola del código anterior podría ser modificado para:

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

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

Una versión más compacta de una expresión de filtrado también puede tener este aspecto:

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

(l for ... ) se llama "expresión generadora", que actúa aquí como un iterador envoltura que filtrará todas las líneas que no necesite de archivo, mientras que la iteración sobre ella. No se debe confundir con la misma cosa en corchetes [l for ... ] plaza que es una "lista de la comprensión" que primero lea todas las líneas del archivo en la memoria y sólo entonces se iniciará la iteración sobre ella.

A veces es posible que desee tener menos de un liney y más legible:

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 los filtros se ejecutarán sobre la marcha en una iteración.

Utilice re.compile("^(?:\s+)*#|(?:\s+)") expresión regular para saltar las nuevas líneas y comentarios.

Yo tiendo a usar

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

Esto ignorará toda la línea, aunque la respuesta que incluye rpartition tiene mi upvote, ya que puede incluir cualquier información desde antes de la #

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top