En Python, je viens de lire une ligne sous forme de fichier texte et je voudrais savoir comment coder pour ignorer les commentaires avec un hachage # au début de la ligne.
Je pense que ça devrait être quelque chose comme ça:
for
if line !contain #
then ...process line
else end for loop
Mais je suis nouveau à Python et je ne connais pas la syntaxe
vous pouvez utiliser commence par ()
par exemple
for line in open("file"):
li=line.strip()
if not li.startswith("#"):
print line.rstrip()
Je vous recommande de ne pas ignorer toute la ligne lorsque vous voyez un caractère #
; ignorez simplement 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
renvoie un Tuple: tout ce qui précède la chaîne de partition, la chaîne de partition et tout ce qui se trouve après la chaîne de partition. Donc, en indexant avec [0]
Nous prenons juste la partie avant 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 ...
Cela fractionne la chaîne sur un caractère "#", puis conserve tout avant la division. L'argument 1
Fait arrêter la méthode .split()
après un split; puisque nous saisissons simplement la sous-chaîne 0 (en indexant avec [0]
), vous obtiendriez la même réponse sans l'argument 1
, mais cela pourrait être un peu plus rapide. (Simplifié par rapport à mon code d'origine grâce à un commentaire de @gnr. Mon code d'origine était plus malpropre sans raison; merci, @gnr.)
Vous pouvez également écrire votre propre version de partition()
. En voici un qui s'appelle 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 si facile de gérer ce cas correctement, alors je l'ai simplement ignoré, mais j'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 acceptiez une chaîne juridique Python citée, car il existe des guillemets multilignes entre guillemets simples et doubles avec une barre oblique inversée échappant aux chaînes de fin de ligne triples ( en utilisant des guillemets simples ou doubles), et même des chaînes brutes! La seule façon possible de gérer correctement tout cela serait une machine à états compliquée.
Mais si nous nous limitons à une simple chaîne entre guillemets, nous pouvons la gérer avec une simple machine à états. Nous pouvons même autoriser un guillemet double entre guillemets 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 compliquer les choses dans une question intitulée "débutant", mais cette machine d'état est relativement simple, et j'espère que ce sera intéressant.
J'arrive tard, mais le problème de la gestion du style Shell (ou python) #
les commentaires sont très courants.
J'utilise du code presque à chaque fois que je lis un fichier texte.
Le problème est qu'il ne gère pas correctement les commentaires entre guillemets ou échappés . Mais cela fonctionne pour les cas simples et c'est facile.
for line in whatever:
line = line.split('#',1)[0].strip()
if not line:
continue
# process line
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 gère non seulement les guillemets et les échappements correctement, mais ajoute également de nombreuses fonctionnalités intéressantes (comme la possibilité d'avoir des fichiers source d'autres fichiers si vous le souhaitez). Je ne l'ai pas testé pour la vitesse sur de gros fichiers, mais il est assez zippé de petites choses.
import shlex
for line in instream:
fields = shlex.split(line, comments=True)
if not fields:
continue
# process list of fields
C'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 renvoie True si la chaîne sur laquelle vous l'appelez commence par la chaîne que vous avez transmise.
Bien que cela soit correct dans certaines circonstances, comme les scripts Shell, cela pose deux problèmes. Tout d'abord, il ne spécifie pas comment ouvrir le fichier. Le mode par défaut pour ouvrir un fichier est 'r'
, Ce qui signifie 'lire le fichier en mode binaire'. Puisque vous attendez un fichier texte, il est préférable de l'ouvrir avec 'rt'
. Bien que cette distinction ne soit pas pertinente sur les systèmes d'exploitation de type UNIX, elle est importante sous Windows (et sur les Mac pré-OS X).
Le deuxième problème est le descripteur de fichier ouvert. La fonction open()
renvoie un objet fichier, et il est considéré comme une bonne pratique de fermer les fichiers lorsque vous en avez terminé avec eux. Pour ce faire, appelez la méthode close()
sur l'objet. Maintenant, Python va probablement faire cela pour vous, éventuellement; in Python sont des références- compté, et lorsque le nombre de références d'un objet atteint zéro, il est libéré, et à un moment donné après qu'un objet est libéré Python appellera son destructeur (une méthode spéciale appelée __del__
) . Notez que j'ai dit probablement: Python a la mauvaise habitude de ne pas réellement appeler le destructeur sur des objets dont le nombre de références tombe à zéro peu de temps avant la fin du programme. Je suppose c'est pressé!
Pour les programmes de courte durée comme les scripts Shell, et en particulier pour les objets fichier, cela n'a pas d'importance. Votre système d'exploitation nettoiera automatiquement tous les descripteurs de fichiers laissés ouverts à la fin du programme. Mais si vous ouvrez le fichier, lisez le contenu, puis commencez un long calcul sans fermer explicitement le descripteur de fichier au préalable, Python est susceptible de laisser le descripteur de fichier ouvert pendant votre calcul. Et c'est une mauvaise pratique .
Cette version fonctionnera dans n'importe quelle version 2.x de Python et corrige les deux problèmes dont j'ai discuté ci-dessus:
f = open(file, 'rt')
for line in f:
if line.startswith('#'):
continue
# PROCESS LINE HERE
f.close()
Il s'agit de la meilleure forme générale pour les anciennes versions de Python.
Comme suggéré par steveha, l'utilisation de la déclaration "with" est désormais considérée comme la meilleure pratique. Si vous utilisez 2.6 ou supérieur, vous devez l'écrire de cette façon:
with open(filename, 'rt') as f:
for line in f:
if line.startswith('#'):
continue
# PROCESS LINE HERE
L'instruction "with" nettoiera le descripteur de fichier pour vous.
Dans votre question, vous avez dit "lignes qui commencent par #", c'est donc ce que je vous ai montré ici. Si vous souhaitez filtrer les lignes commençant par espaces facultatifs et alors un '#', vous devez supprimer les espaces avant de rechercher le '#'. Dans ce cas, vous devez modifier ceci:
if line.startswith('#'):
pour ça:
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 espaces de tête supprimés.
J'ai découvert récemment qu'une fonction de générateur fait un excellent travail. J'ai utilisé des fonctions similaires pour ignorer les lignes de commentaire, les lignes vides, 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
Ceci est réutilisable dans tout mon code, et je peux ajouter toute manipulation/journalisation/etc supplémentaire. ce dont j'ai besoin.
Je sais que c'est un vieux fil, mais c'est une fonction de générateur que j'utilise à mes propres fins. Il supprime les commentaires, peu importe où ils apparaissent dans la ligne, ainsi que la suppression des espaces blancs de début/fin et des 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
Voici le code documenté, qui comprend une démo:
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 __== '__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 normal consiste à supprimer les commentaires d'un fichier (c'est-à-dire un fichier hosts, comme dans mon exemple ci-dessus). Si tel est le cas, la fin du code ci-dessus serait modifiée pour:
if __== '__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 également ressembler à ceci:
for line in (l for l in open(filename) if not l.startswith('#')):
# do something with line
(l for ... )
est appelé "expression de générateur" qui agit ici comme un itérateur d'habillage qui filtrera toutes les lignes inutiles du fichier tout en itérant dessus. Ne le confondez pas avec la même chose dans des paniers carrés [l for ... ]
qui est une "compréhension de liste" qui lira d'abord toutes les lignes du fichier en mémoire et commencera ensuite à itérer dessus.
Parfois, vous voudrez peut-être qu'il soit moins linéaire 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 en une seule itération.
Utilisez regex re.compile("^(?:\s+)*#|(?:\s+)")
pour ignorer les nouvelles lignes et les commentaires.
J'ai tendance à utiliser
for line in lines:
if '#' not in line:
#do something
Cela ignorera toute la ligne, bien que la réponse qui inclut la répartition ait mon vote positif car elle peut inclure toutes les informations d'avant le #