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

Самый простой способ игнорировать пустые строки при чтении файла в Python

У меня есть код, который читает файл имен и создает список:

names_list = open("names", "r").read().splitlines()

Каждое имя отделяется новой строкой, например:

Allman
Atkinson

Behlendorf 

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

Мне просто интересно, есть ли более Pythonic способ сделать это?

4b9b3361

Ответ 1

Я бы складывал выражения генератора:

with open(filename) as f_in:
    lines = (line.rstrip() for line in f_in) # All lines including the blank ones
    lines = (line for line in lines if line) # Non-blank lines

Теперь lines - это все непустые строки. Это избавит вас от необходимости дважды называть полосу на линии. Если вам нужен список строк, вы можете просто сделать:

with open(filename) as f_in:
    lines = (line.rstrip() for line in f_in) 
    lines = list(line for line in lines if line) # Non-blank lines in a list

Вы также можете сделать это в однострочном (исключая with), но это не более эффективно и труднее читать:

with open(filename) as f_in:
    lines = list(line for line in (l.strip() for l in f_in) if line)

Обновление:

Я согласен, что это уродливо из-за повторения жетонов. Вы можете просто написать генератор, если хотите:

def nonblank_lines(f):
    for l in f:
        line = l.rstrip()
        if line:
            yield line

Затем назовите его так:

with open(filename) as f_in:
    for line in nonblank_lines(f_in):
        # Stuff

update 2:

with open(filename) as f_in:
    lines = filter(None, (line.rstrip() for line in f_in))

и на CPython (с детерминированным подсчетом ссылок)

lines = filter(None, (line.rstrip() for line in open(filename)))

В Python 2 используйте itertools.ifilter, если вы хотите генератор и в Python 3, просто передайте все это в list, если вам нужен список.

Ответ 2

Вы можете использовать понимание списка:

with open("names", "r") as f:
    names_list = [line.strip() for line in f if line.strip()]

Обновлено: Удалено ненужное readlines().

Чтобы избежать вызова line.strip() дважды, вы можете использовать генератор:

names_list = [l for l in (line.strip() for line in f) if l]

Ответ 3

Если вы хотите, вы можете просто добавить то, что у вас было в понимании списка:

names_list = [line for line in open("names.txt", "r").read().splitlines() if line]

или

all_lines = open("names.txt", "r").read().splitlines()
names_list = [name for name in all_lines if name]

splitlines() уже удалил концы строк.

Я не думаю, что это так ясно, как просто цикл, хотя:

names_list = []
with open('names.txt', 'r') as _:
    for line in _:
        line = line.strip()
        if line:
            names_list.append(line)

Edit:

Хотя фильтр выглядит вполне читаемым и кратким:

names_list = filter(None, open("names.txt", "r").read().splitlines())

Ответ 4

Когда обработка текста должна выполняться только для извлечения данных из него, я всегда думаю сначала о регулярных выражениях, потому что:

  • насколько я знаю, для этого были созданы регулярные выражения

  • Итерирование по линиям кажется неуклюжим для меня: по сути, это поиск новых строк, а затем поиск данных для извлечения в каждой строке; что делает два запроса вместо прямого единственного с регулярным выражением

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

Для обсуждаемой здесь задачи регулярное выражение быстро и легко писать:

import re
names = re.findall('\S+',open(filename).read())

Я сравнивал скорости нескольких решений:

import re
from time import clock

A,AA,B1,B2,BS,reg = [],[],[],[],[],[]
D,Dsh,C1,C2 = [],[],[],[]
F1,F2,F3  = [],[],[]

def nonblank_lines(f):
    for l in f:
        line = l.rstrip()
        if line:  yield line

def short_nonblank_lines(f):
    for l in f:
        line = l[0:-1]
        if line:  yield line

for essays in xrange(50):

    te = clock()
    with open('raa.txt') as f:
        names_listA = [line.strip() for line in f if line.strip()] # Felix Kling
    A.append(clock()-te)

    te = clock()
    with open('raa.txt') as f:
        names_listAA = [line[0:-1] for line in f if line[0:-1]] # Felix Kling with line[0:-1]
    AA.append(clock()-te)

    #-------------------------------------------------------
    te = clock()
    with open('raa.txt') as f_in:
        namesB1 = [ name for name in (l.strip() for l in f_in) if name ] # aaronasterling without list()
    B1.append(clock()-te)

    te = clock()
    with open('raa.txt') as f_in:
        namesB2 = [ name for name in (l[0:-1] for l in f_in) if name ] # aaronasterling without list() and with line[0:-1]
    B2.append(clock()-te)

    te = clock()
    with open('raa.txt') as f_in:
        namesBS = [ name for name in f_in.read().splitlines() if name ] # a list comprehension with read().splitlines()
    BS.append(clock()-te)

    #-------------------------------------------------------
    te = clock()
    with open('raa.txt') as f:
        xreg = re.findall('\S+',f.read()) #  eyquem
    reg.append(clock()-te)

    #-------------------------------------------------------
    te = clock()
    with open('raa.txt') as f_in:
        linesC1 = list(line for line in (l.strip() for l in f_in) if line) # aaronasterling
    C1.append(clock()-te)

    te = clock()
    with open('raa.txt') as f_in:
        linesC2 = list(line for line in (l[0:-1] for l in f_in) if line) # aaronasterling  with line[0:-1]
    C2.append(clock()-te)

    #-------------------------------------------------------
    te = clock()
    with open('raa.txt') as f_in:
        yD = [ line for line in nonblank_lines(f_in)  ] # aaronasterling  update
    D.append(clock()-te)

    te = clock()
    with open('raa.txt') as f_in:
        yDsh = [ name for name in short_nonblank_lines(f_in)  ] # nonblank_lines with line[0:-1]
    Dsh.append(clock()-te)

    #-------------------------------------------------------
    te = clock()
    with open('raa.txt') as f_in:
        linesF1 = filter(None, (line.rstrip() for line in f_in)) # aaronasterling update 2
    F1.append(clock()-te)

    te = clock()
    with open('raa.txt') as f_in:
        linesF2 = filter(None, (line[0:-1] for line in f_in)) # aaronasterling update 2 with line[0:-1]
    F2.append(clock()-te)

    te = clock()
    with open('raa.txt') as f_in:
        linesF3 =  filter(None, f_in.read().splitlines()) # aaronasterling update 2 with read().splitlines()
    F3.append(clock()-te)


print 'names_listA == names_listAA==namesB1==namesB2==namesBS==xreg\n  is ',\
       names_listA == names_listAA==namesB1==namesB2==namesBS==xreg
print 'names_listA == yD==yDsh==linesC1==linesC2==linesF1==linesF2==linesF3\n  is ',\
       names_listA == yD==yDsh==linesC1==linesC2==linesF1==linesF2==linesF3,'\n\n\n'


def displ((fr,it,what)):  print fr + str( min(it) )[0:7] + '   ' + what

map(displ,(('* ', A,    '[line.strip() for line in f if line.strip()]               * Felix Kling\n'),

           ('  ', B1,   '    [name for name in (l.strip() for l in f_in) if name ]    aaronasterling without list()'),
           ('* ', C1,   'list(line for line in (l.strip() for l in f_in) if line)   * aaronasterling\n'),          

           ('* ', reg,  're.findall("\S+",f.read())                                 * eyquem\n'),

           ('* ', D,    '[ line for line in       nonblank_lines(f_in)  ]           * aaronasterling  update'),
           ('  ', Dsh,  '[ line for line in short_nonblank_lines(f_in)  ]             nonblank_lines with line[0:-1]\n'),

           ('* ', F1 ,  'filter(None, (line.rstrip() for line in f_in))             * aaronasterling update 2\n'),

           ('  ', B2,   '    [name for name in (l[0:-1]   for l in f_in) if name ]    aaronasterling without list() and with line[0:-1]'),
           ('  ', C2,   'list(line for line in (l[0:-1]   for l in f_in) if line)     aaronasterling  with line[0:-1]\n'),

           ('  ', AA,   '[line[0:-1] for line in f if line[0:-1]  ]                   Felix Kling with line[0:-1]\n'),

           ('  ', BS,   '[name for name in f_in.read().splitlines() if name ]        a list comprehension with read().splitlines()\n'),

           ('  ', F2 ,  'filter(None, (line[0:-1] for line in f_in))                  aaronasterling update 2 with line[0:-1]'),

           ('  ', F3 ,  'filter(None, f_in.read().splitlines()                        aaronasterling update 2 with read().splitlines()'))
    )

Решение с регулярным выражением является простым и опрятным. Хотя, это не один из самых быстрых. Решение aaronasterling с filter() для меня неожиданно быстро (я не знал об этой скорости фильтра()), а время оптимизированных решений снижается до 27% самого большого времени. Интересно, что делает чудо ассоциации фильтров-разделенных линий:

names_listA == names_listAA==namesB1==namesB2==namesBS==xreg
  is  True
names_listA == yD==yDsh==linesC1==linesC2==linesF1==linesF2==linesF3
  is  True 



* 0.08266   [line.strip() for line in f if line.strip()]               * Felix Kling

  0.07535       [name for name in (l.strip() for l in f_in) if name ]    aaronasterling without list()
* 0.06912   list(line for line in (l.strip() for l in f_in) if line)   * aaronasterling

* 0.06612   re.findall("\S+",f.read())                                 * eyquem

* 0.06486   [ line for line in       nonblank_lines(f_in)  ]           * aaronasterling  update
  0.05264   [ line for line in short_nonblank_lines(f_in)  ]             nonblank_lines with line[0:-1]

* 0.05451   filter(None, (line.rstrip() for line in f_in))             * aaronasterling update 2

  0.04689       [name for name in (l[0:-1]   for l in f_in) if name ]    aaronasterling without list() and with line[0:-1]
  0.04582   list(line for line in (l[0:-1]   for l in f_in) if line)     aaronasterling  with line[0:-1]

  0.04171   [line[0:-1] for line in f if line[0:-1]  ]                   Felix Kling with line[0:-1]

  0.03265   [name for name in f_in.read().splitlines() if name ]        a list comprehension with read().splitlines()

  0.03638   filter(None, (line[0:-1] for line in f_in))                  aaronasterling update 2 with line[0:-1]
  0.02198   filter(None, f_in.read().splitlines()                        aaronasterling update 2 with read().splitlines()

Но эта проблема является конкретной, самой простой из всего: только одно имя в каждой строке. Таким образом, решениями являются только игры с линиями, разбиениями и сокращениями [0: -1].

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

ИЗМЕНИТЬ

Я забыл сказать, что я использую Python 2.7, и я измерил вышеупомянутые времена с файлом, содержащим 500 раз следующую цепочку

SMITH
JONES
WILLIAMS
TAYLOR
BROWN
DAVIES
EVANS
WILSON
THOMAS
JOHNSON

ROBERTS
ROBINSON
THOMPSON
WRIGHT
WALKER
WHITE
EDWARDS
HUGHES
GREEN
HALL

LEWIS
HARRIS
CLARKE
PATEL
JACKSON
WOOD
TURNER
MARTIN
COOPER
HILL

WARD
MORRIS
MOORE
CLARK
LEE
KING
BAKER
HARRISON
MORGAN
ALLEN

JAMES
SCOTT
PHILLIPS
WATSON
DAVIS
PARKER
PRICE
BENNETT
YOUNG
GRIFFITHS

MITCHELL
KELLY
COOK
CARTER
RICHARDSON
BAILEY
COLLINS
BELL
SHAW
MURPHY

MILLER
COX
RICHARDS
KHAN
MARSHALL
ANDERSON
SIMPSON
ELLIS
ADAMS
SINGH

BEGUM
WILKINSON
FOSTER
CHAPMAN
POWELL
WEBB
ROGERS
GRAY
MASON
ALI

HUNT
HUSSAIN
CAMPBELL
MATTHEWS
OWEN
PALMER
HOLMES
MILLS
BARNES
KNIGHT

LLOYD
BUTLER
RUSSELL
BARKER
FISHER
STEVENS
JENKINS
MURRAY
DIXON
HARVEY

Ответ 5

@С. Лотт

Следующий код обрабатывает строки по одному и дает результат, который не запоминается:

filename = 'english names.txt'

with open(filename) as f_in:
    lines = (line.rstrip() for line in f_in)
    lines = (line for line in lines if line)
    the_strange_sum = 0
    for l in lines:
        the_strange_sum += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.find(l[0])

print the_strange_sum

Таким образом, генератор (line.rstrip() для строки в f_in) вполне приемлем, чем функция nonblank_lines().

Ответ 6

Что касается модуля LineSentence, он будет игнорировать такие строки:

Основы: объект

Простой формат: одно предложение = одна строка; слова уже предварительно обработаны и разделены пробелами.

Источник может быть либо строкой, либо файловым объектом. Обрезать файл до первых предельных строк (или не обрезать, если предел равен None, по умолчанию).

from gensim.models.word2vec import LineSentence
text = LineSentence('text.txt')

Ответ 7

Я думаю, что есть простое решение, которое я недавно использовал, пройдя через так много ответов здесь.

with open(file_name) as f_in:   
    for line in f_in:
        if len(line.split()) == 0:
            continue

Это просто делает ту же работу, игнорируя все пустые строки.

Ответ 8

Почему вы все идете трудным путем?

with open("myfile") as myfile:
    nonempty = filter(str.rstrip, myfile)

Преобразуйте непустые в список, если у вас есть желание сделать это, хотя я настоятельно рекомендую оставить непустой генератор таким же, как в Python 3.x

В Python 2.x вместо этого вы можете использовать itertools.ifilter.