Domanda

In Python, ho appena letto una riga di un file di testo e mi piacerebbe sapere come il codice di ignorare i commenti con un carattere # all'inizio della riga.

Penso che dovrebbe essere qualcosa di simile a questo:

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

Ma io sono nuovo di Python e non so la sintassi

È stato utile?

Soluzione

è possibile utilizzare startswith ()

es

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

Altri suggerimenti

Vi consiglio di non ignorare tutta la linea quando si vede un personaggio #; basta ignorare il resto della linea. Si può fare facilmente con una funzione di metodo di stringa denominata partition:

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

partition restituisce una tupla: tutto prima della stringa di partizione, la stringa di partizione, e tutto ciò dopo la stringa di partizione. Così, indicizzando con [0] prendiamo solo la parte prima della stringa partizione.

EDIT: Se si utilizza una versione di Python che non ha partition(), qui è il codice è possibile utilizzare:

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

Questo divide la stringa su un carattere '#', quindi mantiene tutto prima della scissione. L'argomento 1 rende il metodo stop .split() dopo uno spaccato; dal momento che ci sono solo afferrando la stringa 0a (indicizzando con [0]) si ottiene la stessa risposta senza l'argomento 1, ma questo potrebbe essere un po 'più veloce. (Semplificato dal mio codice grazie originali per un commento da @gnr Il mio codice originale era di Messier per nessuna buona ragione,.. Grazie, @gnr)

Si potrebbe anche solo scrivere la propria versione di partition(). Qui è uno chiamato 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 ha osservato che '#' può apparire all'interno di una stringa. Non è che facile da gestire questo caso in modo corretto, quindi ho solo ignorato, ma avrei dovuto dire qualcosa.

Se il file di input ha regole abbastanza semplice per stringhe tra virgolette, questo non è difficile. Sarebbe difficile se si accetta qualsiasi Python citato stringa legale, perché ci sono tra apici singoli, doppi apici, le citazioni più righe con un backslash sfuggire la linea di fine, stringhe tra virgolette triple (con le virgolette singole o doppie), e stringhe anche crudo! L'unico modo possibile per gestire correttamente tutti che sarebbe una macchina a stati complicata.

Ma se ci limitiamo ad una semplice stringa tra virgolette, siamo in grado di gestire la cosa con una semplice macchina a stati. Possiamo anche permettere una doppia citazione backslash virgolette all'interno della stringa.

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

Non ho voglia di ottenere questo complicato in una domanda tagged "principiante", ma questa macchina statale è ragionevolmente semplice, e spero che sarà interessante.

Vengo a questo ritardo, ma il problema di gestire stile shell # (o pitone) commenti è molto comune.

Ho usato un codice quasi ogni volta che ho letto un file di testo.
Il problema è che non gestisce commenti quotati o sfuggiti correttamente . Ma funziona per i casi semplici ed è facile.

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

Una soluzione più robusta è quella di utilizzare 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

Questo approccio shlex non solo gestisce le citazioni e fugge correttamente, si aggiunge un sacco di funzionalità fresco (come la possibilità di avere i file di origine di altri file, se si desidera). Non ho ancora testato per velocità su file di grandi dimensioni, ma è abbastanza scattante di piccole cose.

Il caso comune quando si sta anche dividendo ogni linea di ingresso in campi (su spazi bianchi) è ancora più semplice:

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

Questo è il più breve possibile:

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

Il startswith() metodo su una stringa restituisce True se la stringa di chiamata su inizia con la stringa che hai passato.

Mentre questo è bene, in alcune circostanze, come gli script di shell, ha due problemi.Primo, non specifica come aprire il file.La modalità di default per l'apertura di un file è 'r', che significa "leggere il file in modalità binaria'.Visto che sei in attesa di un file di testo è meglio aprire con 'rt'.Anche se questa distinzione è irrilevante su sistemi operativi UNIX-like, è importante che su Windows (e pre-OS X Mac).

Il secondo problema è l'handle di file aperto.Il open() la funzione restituisce un oggetto di tipo file, ed è considerata una buona pratica di chiudere il file quando hai finito con loro.Per fare questo, chiamare il close() metodo sull'oggetto.Ora, Python probabilmente fare questo per voi, alla fine; in Python sono oggetti di riferimento contati, e quando un oggetto di riferimento per il conteggio va a zero non viene liberato, e a un certo punto, dopo che un oggetto è liberato Python chiamerà il distruttore (un metodo speciale chiamato __del__).Nota che ho detto probabilmente: Python ha la cattiva abitudine di non effettivamente chiamare il distruttore sugli oggetti di cui conteggio di riferimento scende a zero, poco prima che il programma termina.Credo che in fretta e furia!

Per la breve durata dei programmi come script di shell, e in particolare per gli oggetti file, questo non importa.Il sistema operativo pulire automaticamente il backup di qualsiasi file handle aperto quando il programma termina.Ma se si apre il file, leggere il contenuto, poi ha iniziato un lungo calcolo senza dover chiudere l'handle di file di prima, Python è probabile che lasciano l'handle di file aperti durante il calcolo.E questa è una cattiva pratica.

Questa versione funziona in qualsiasi 2.x la versione di Python, e risolve entrambi i problemi che ho discusso in precedenza:

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

Questa è la migliore forma generale per le vecchie versioni di Python.

Come suggerito da steveha, utilizzando il "con" l'istruzione è oggi considerata la migliore pratica.Se si utilizza 2.6 o superiore si dovrebbe scrivere in questo modo:

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

Il "con" istruzione per ripulire il file di gestire per voi.

Nella tua domanda hai detto "le righe che iniziano con #", in modo che quello che vi ho mostrato qui.Se si desidera filtrare le righe che iniziano con opzionale spazi e quindi un '#', si dovrebbe striscia lo spazio prima di cercare il '#'.In questo caso, è necessario modificare questa:

    if line.startswith('#'):

per questo:

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

In Python, le stringhe sono immutabili, quindi questo non cambia il valore di line.Il lstrip() il metodo restituisce una copia della stringa con tutti i principali spazi rimosso.

Ho scoperto di recente che un generatore di funzione fa un grande lavoro di questo.Io l'ho utilizzato con funzioni simili a saltare le righe di commento, le righe vuote, etc.

Posso definire la mia funzione come

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

In questo modo, posso solo fare

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

Questo è riutilizzabile in tutti il mio codice, e posso aggiungere qualsiasi ulteriore gestione/registrazione/etc.che ho bisogno.

So che questo è un vecchio filo, ma questa è una funzione di generatore che ho utilizzare per i miei scopi. Si spoglia commenti, non importa dove visualizzato nella riga, nonché di stripping principale / spazi finali e righe vuote. Il seguente testo sorgente:

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

produrrà:

host02
host03
host04

Codice Qui è documentato, che comprende 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))

Il caso di uso normale sarà mettere a nudo i commenti da un file (vale a dire, un file host, come nel mio esempio di cui sopra). Se questo è il caso, allora la coda del codice precedente verrebbe modificato da:

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

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

Una versione più compatta di un'espressione di filtro può anche assomigliare a questo:

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

(l for ... ) è chiamato "generatore di espressione", che agisce qui come un iteratore involucro che filtrerà tutte le righe non necessarie dal file mentre l'iterazione su di esso. Da non confondere con la stessa cosa in parentesi quadre [l for ... ] che è un "di lista", che leggono prima tutte le righe del file in memoria e solo allora comincerà l'iterazione su di esso.

A volte si potrebbe desiderare di avere meno di un Liney e più leggibile:

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

Tutti i filtri saranno eseguite al volo in un'iterazione.

Utilizza re.compile("^(?:\s+)*#|(?:\s+)") regex di saltare le nuove linee e commenti.

Io tendo ad usare

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

Questa ignorerà tutta la linea, anche se la risposta che include rpartition ha il mio upvote in quanto può includere tutte le informazioni da prima della #

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top