Подтвердить что ты не робот

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

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

Я думаю, что это должно быть примерно так:

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

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

4b9b3361

Ответ 1

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

например,

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

Ответ 2

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

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

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

EDIT: Если вы используете версию 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 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

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

Ответ 3

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

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

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

Более надежное решение - использовать 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

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

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

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

Ответ 4

Это кратчайшая возможная форма:

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

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

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

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

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

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

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

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

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

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

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

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

    if line.startswith('#'):

:

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

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

Ответ 5

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

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

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

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

Ответ 6

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

# 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 non-blank line from ''item'' with comments and
            leading/trailing whitespace removed.

    """

    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)

Ответ 7

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

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

Все фильтры будут выполняться "на лету" на одной итерации.

Ответ 8

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

Ответ 9

Я стараюсь использовать

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

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