Question

En Python, je viens de lire une ligne forme un fichier texte et je voudrais savoir comment le code d'ignorer les commentaires avec un hachage # au début de la ligne.

Je pense que ce devrait être quelque chose comme ceci:

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

Mais je suis nouveau à Python et je ne sais pas la syntaxe

Était-ce utile?

La solution

vous pouvez utiliser startswith ()

par exemple

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

Autres conseils

Je vous recommande de ne pas ignorer toute la ligne quand vous voyez un caractère #; ignorer le reste de la ligne. Vous pouvez le faire facilement avec une fonction de méthode de chaîne appelée partition:

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

partition retourne un tuple: tout avant la chaîne de partition, la chaîne de partition, et tout ce qui suit la chaîne de partition. Ainsi, par l'indexation avec [0] nous prenons juste la partie avant que la chaîne de partition.

EDIT: Si vous utilisez une version de Python qui n'a pas partition(), voici le code que vous pouvez utiliser:

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

fractionne la chaîne sur un caractère « # », conserve alors tout avant la scission. L'argument 1 fait de l'arrêt de la méthode de .split() après une division; puisque nous sommes en train de l'accaparement de la sous-chaîne 0e (par l'indexation avec [0]) vous obtiendrez la même réponse sans l'argument 1, mais cela pourrait être un peu plus rapide. (Simplifié de mon code d'origine grâce à un commentaire de @gnr Mon code original était messier sans raison,.. Merci, @gnr)

Vous pouvez aussi simplement écrire votre propre version de partition(). Voici un appelé 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 a noté que '#' peut apparaître dans une chaîne. Ce n'est pas facile à manipuler correctement ce cas, donc je viens de l'ignorer, mais je aurais dû dire quelque chose.

Si votre fichier d'entrée a des règles assez simples pour les chaînes entre guillemets, ce n'est pas difficile. Il serait difficile si vous avez accepté une chaîne entre guillemets Python juridique, parce qu'il ya des guillemets simples, entre guillemets, guillemets multilignes avec un backslash la fin de ligne, les chaînes entre guillemets triples (à l'aide de simples ou doubles guillemets), et même les chaînes premières! La seule façon possible de traiter correctement tout ce qui serait une machine d'état compliqué.

Mais si on se limite à une simple chaîne entre guillemets, nous pouvons traiter avec une simple machine d'état. On peut même permettre un guillemet cité backslash-dans la chaîne.

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

Je ne voulais pas vraiment obtenir ce complexe dans une question tagged « débutant », mais cette machine d'état est assez simple, et j'espère que ce sera intéressant.

Je viens à cette fin, mais le problème de la manipulation style shell (ou le style python) # commentaires est très commun.

J'utilise un code presque chaque fois que je lis un fichier texte.
Le problème est qu'il ne gère pas échappées commentaires correctement . Mais cela fonctionne pour les cas simples et est facile.

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

Une solution plus robuste consiste à utiliser 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

Cette approche shlex traite non seulement des citations et échappe correctement, il ajoute beaucoup de fonctionnalités frais (comme la possibilité d'avoir une source de fichiers d'autres fichiers si vous voulez). Je ne l'ai pas testé pour la vitesse sur de gros fichiers, mais il est assez zippy de petites choses.

Le cas fréquent lorsque vous partagez également chaque ligne d'entrée dans les champs (sur les espaces) est encore plus simple:

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

Ceci est la forme la plus courte possible:

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

La méthode startswith() sur une chaîne retourne Vrai si la chaîne que vous appelez sur commence par la chaîne transmise.

Bien que cela est acceptable dans certaines circonstances comme des scripts shell, il a deux problèmes. Tout d'abord, il ne précise pas comment ouvrir le fichier. Le mode par défaut pour l'ouverture d'un fichier est 'r', qui signifie «lire le fichier en mode binaire. Puisque vous vous attendez à un fichier texte, il est préférable de l'ouvrir avec 'rt'. Bien que cette distinction est hors de propos sur les systèmes d'exploitation de type UNIX, il est important sur Windows (et sur Mac OS pré-X).

Le deuxième problème est le descripteur de fichier ouvert. La fonction open() retourne un objet fichier, et il est considéré comme une bonne pratique de fermer les fichiers lorsque vous avez terminé avec eux. Pour ce faire, appelez la méthode close() sur l'objet. Maintenant, Python probablement faire pour vous, éventuellement; dans les objets Python sont comptés référence, et lorsque le nombre de référence d'un objet passe à zéro, il devient Freed, et à un certain Point après qu'un objet est libéré Python appellera son destructor (un procédé spécial appelé __del__). Notez que je l'ai dit sans doute: Python a une mauvaise habitude de ne pas remettre en fait le destructor sur des objets dont le nombre de référence tombe à zéro peu avant que le programme se termine. Je suppose que c'est pressé!

Pour les programmes de courte durée comme les scripts shell, et en particulier pour les objets de fichiers, cela n'a aucune importance. Votre système d'exploitation nettoiera automatiquement tous les fichiers poignées reste ouvert lorsque le programme se termine. Mais si vous ouvrez le fichier, lire le contenu, puis a commencé un calcul à long sans fermer explicitement le descripteur de fichier d'abord, Python est susceptible de quitter le fichier ouvert lors de votre calcul. Et c'est une mauvaise pratique.

Cette version fonctionne dans une version 2.x de Python, et résout les problèmes évoqués ci-dessus, je:

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

est la meilleure forme générale pour les anciennes versions de Python.

Comme suggéré par steveha, en utilisant la mention « avec » est maintenant considérée comme la meilleure pratique. Si vous utilisez 2.6 ou au-dessus, vous devriez l'écrire de cette façon:

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

Le « avec » déclaration nettoiera la poignée de fichiers pour vous.

Dans votre question, vous avez dit « les lignes commençant par # », de sorte que ce que je vous ai montré ici. Si vous souhaitez filtrer les lignes qui commencent par en option des espaces et puis un « # », vous devez enlever les espaces blancs avant de chercher le « # ». Dans ce cas, vous devez changer ceci:

    if line.startswith('#'):

à ceci:

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

En Python, les chaînes sont immuables, donc cela ne change pas la valeur de line. La méthode lstrip() renvoie une copie de la chaîne avec tous ses principaux espaces supprimés.

J'ai trouvé récemment qu'une fonction génératrice fait un excellent travail de cela. Je l'ai utilisé des fonctions similaires pour sauter les lignes de commentaires, lignes blanches, etc.

Je définis ma fonction comme

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

De cette façon, je peux juste faire

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

est réutilisable dans tout mon code, et je peux ajouter toute manipulation supplémentaire / enregistrement / etc. que j'ai besoin.

Je sais que c'est un vieux fil, mais c'est une fonction génératrice que je utiliser pour mes propres fins. Il bandes commentaires peu importe où ils apparaissent dans la ligne, ainsi que de décapage premier / arrière espaces et lignes vides. Le texte source suivant:

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

donnera:

host02
host03
host04

code ici est documenté, qui comprend une démonstration:

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

Le cas d'utilisation normale sera de dépouiller les commentaires d'un fichier (par exemple, un fichier d'hôtes, comme dans mon exemple ci-dessus). Si tel est le cas, la fin de la queue du code ci-dessus serait modifié pour:

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

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

Une version plus compacte d'une expression de filtrage peut aussi ressembler à ceci:

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

(l for ... ) est appelé « l'expression du générateur » qui agit ici comme un itérateur d'emballage qui filtrera toutes les lignes inutiles de fichier en itérer dessus. Ne pas confondre avec la même chose dans brakets carré [l for ... ] qui est une « compréhension de la liste » qui va d'abord lire toutes les lignes du fichier en mémoire et alors seulement commencer itérer dessus.

Parfois, vous voudrez peut-être avoir moins d'un liney et plus lisible:

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

Tous les filtres seront exécutés à la volée dans une itération.

Utilisez regex re.compile("^(?:\s+)*#|(?:\s+)") pour sauter les nouvelles lignes et commentaires.

Je tends à utiliser

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

ignorera toute la ligne, bien que la réponse qui comprend rpartition a mon upvote car il peut inclure toute information d'avant la #

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top