Питон:Как игнорировать строки #comment при чтении в файле

StackOverflow https://stackoverflow.com/questions/1706198

Вопрос

В Python я только что прочитал строку из текстового файла, и я хотел бы знать, как закодировать игнорирование комментариев с хэшем # в начале строки.

Я думаю, это должно быть что-то вроде этого:

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

Но я новичок в Python и не знаю синтаксиса

Это было полезно?

Решение

вы можете использовать начинается с()

например

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

Другие советы

Я рекомендую вам не игнорировать всю строку целиком, когда вы видите # характер;просто игнорируйте остальную часть строки.Вы можете легко сделать это с помощью вызываемой функции string method partition:

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

partition возвращает кортеж:все, что находится перед строкой раздела, строка раздела и все, что находится после строки раздела.Итак, путем индексации с помощью [0] мы берем только ту часть, которая находится перед строкой раздела.

Редактировать:Если вы используете версию Python, которая не имеет partition(), вот код, который вы могли бы использовать:

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

Это разбивает строку на символ '#', затем сохраняет все до разделения.Тот самый 1 аргумент приводит к тому, что .split() метод останавливается после одного разделения;поскольку мы просто захватываем 0-ю подстроку (путем индексации с помощью [0]) вы получили бы тот же ответ без 1 аргумент, но это могло бы быть немного быстрее.(Упрощено по сравнению с моим исходным кодом благодаря комментарию от @gnr.Мой исходный код был более запутанным без всякой уважительной причины;спасибо, @gnr.)

Вы также могли бы просто написать свою собственную версию partition().Вот один из них называется 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 отметил, что '#' может появляться внутри строки.Не так-то просто правильно разобраться с этим делом, поэтому я просто проигнорировал это, но я должен был что-то сказать.

Если ваш входной файл содержит достаточно простые правила для строк, заключенных в кавычки, это несложно.Было бы сложно, если бы вы приняли любую легальную строку в кавычках Python, потому что существуют одинарные кавычки, двойные кавычки, многострочные кавычки с обратной косой чертой, экранирующей конец строки, строки в тройных кавычках (с использованием одинарных или двойных кавычек) и даже необработанные строки!Единственным возможным способом корректно обработать все это был бы сложный конечный автомат.

Но если мы ограничимся простой строкой, заключенной в кавычки, мы сможем обработать ее с помощью простого конечного автомата.Мы даже можем разрешить заключать строку в двойные кавычки с обратной косой чертой внутри строки.

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

Я действительно не хотел усложнять вопрос с пометкой "новичок", но этот конечный автомат достаточно прост, и я надеюсь, что он будет интересным.

Я опоздал, но проблема с обработкой стиля оболочки (или стиля Python) # комментарии очень распространены.

Я использую какой-то код почти каждый раз, когда читаю текстовый файл.
Проблема в том, что он неправильно обрабатывает цитируемые или экранированные комментарии..Но это работает для простых случаев и легко.

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

Этот подход shlex не только правильно обрабатывает кавычки и экранирует их, но и добавляет множество интересных функций (например, возможность использовать файлы в качестве источника других файлов, если хотите).Я не проверял его скорость на больших файлах, но с мелкими файлами он достаточно быстр.

Общий случай, когда вы также разделяете каждую входную строку на поля (по пробелам), еще проще:

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

Это самая короткая из возможных форм:

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

Тот самый startswith() метод для строки возвращает True, если строка, в которой вы его вызываете, начинается со строки, которую вы передали.

Хотя это нормально в некоторых обстоятельствах, таких как сценарии командной оболочки, у этого есть две проблемы.Во-первых, в нем не указано, как открыть файл.Режимом открытия файла по умолчанию является 'r', что означает "прочитать файл в двоичном режиме".Поскольку вы ожидаете получить текстовый файл, лучше открыть его с помощью 'rt'.Хотя это различие неуместно в UNIX-подобных операционных системах, оно важно в Windows (и на Mac до OS X).

Вторая проблема - это дескриптор открытого файла.Тот самый open() функция возвращает объект file, и считается хорошей практикой закрывать файлы, когда вы закончите с ними.Чтобы сделать это, вызовите close() метод для объекта.Теперь Python будет вероятно сделаю это для тебя, в конце концов; в Python объекты подсчитываются по ссылкам, и когда счетчик ссылок объекта становится равным нулю, он освобождается, и в какой-то момент после освобождения объекта Python вызовет его деструктор (вызываемый специальный метод __del__).Обратите внимание, что я сказал вероятно: У Python есть плохая привычка фактически не вызывать деструктор для объектов, количество ссылок на которые падает до нуля незадолго до завершения программы.Я думаю, это в спешке!

Для недолговечных программ, таких как сценарии командной оболочки, и особенно для файловых объектов, это не имеет значения.Ваша операционная система автоматически очистит все дескрипторы файлов, оставшиеся открытыми, когда программа завершит работу.Но если вы открыли файл, прочитали содержимое, а затем начали длительное вычисление без предварительного явного закрытия дескриптора файла, Python, скорее всего, оставит дескриптор файла открытым во время ваших вычислений.И это плохая практика.

Эта версия будет работать в любой версии Python 2.x и устраняет обе проблемы, которые я обсуждал выше:

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

Это наилучшая общая форма для старых версий Python.

Как предложил steveha, использование инструкции "with" в настоящее время считается наилучшей практикой.Если вы используете версию 2.6 или выше, вы должны написать это следующим образом:

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

Оператор "with" очистит дескриптор файла для вас.

В своем вопросе вы сказали "строки, начинающиеся с #", так что это то, что я показал вам здесь.Если вы хотите отфильтровать строки, начинающиеся с необязательный пробел и тогда a '#', вы должны убрать пробел, прежде чем искать '#'.В таком случае вам следует изменить это:

    if line.startswith('#'):

к этому:

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

В Python строки неизменяемы, поэтому это не меняет значения line.Тот самый lstrip() метод возвращает копию строки со всеми удаленными начальными пробелами.

Недавно я обнаружил, что функция генератора отлично справляется с этой задачей.Я использовал аналогичные функции для пропуска строк комментариев, пустых строк и т. д.

Я определяю свою функцию как

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

Таким образом, я могу просто сделать

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

Это можно повторно использовать во всем моем коде, и я могу добавить любую дополнительную обработку/ведение журнала/и т. д.что мне нужно.

Я знаю, что это старый поток, но это функция генератора, которую я использую для своих собственных целей.Он удаляет комментарии независимо от того, где они появляются в строке, а также удаляет начальные / завершающие пробелы и пустые строки.Следующий исходный текст:

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

уступит:

host02
host03
host04

Вот документированный код, который включает в себя демо-версию:

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

Обычным вариантом использования будет удаление комментариев из файла (т. Е. файла hosts, как в моем примере выше).Если это так, то конечная часть приведенного выше кода будет изменена следующим образом:

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

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

Более компактная версия выражения фильтрации также может выглядеть так:

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

(l for ... ) называется «выражением-генератором», которое действует здесь как оборачивающий итератор, который отфильтровывает все ненужные строки из файла во время итерации по нему.Не путайте с тем же в квадратных скобках. [l for ... ] это «понимание списка», которое сначала прочитает все строки из файла в память и только затем начнет перебирать их.

Иногда вам может потребоваться сделать его менее однострочным и более читабельным:

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

Все фильтры будут выполняться «на лету» за одну итерацию.

Используйте регулярное выражение re.compile("^(?:\s+)*#|(?:\s+)") чтобы пропустить новые строки и комментарии.

Я склонен использовать

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

Это будет игнорировать всю строку, хотя ответ, включающий rpartition, имеет мой плюс, поскольку он может включать любую информацию до #

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top