Самый простой способ игнорировать пустые строки при чтении файла в Python
У меня есть код, который читает файл имен и создает список:
names_list = open("names", "r").read().splitlines()
Каждое имя отделяется новой строкой, например:
Allman
Atkinson
Behlendorf
Я хочу игнорировать любые строки, которые содержат только пробелы. Я знаю, что могу сделать это, создав цикл и проверив каждую прочитанную строку, а затем добавив ее в список, если он не пустой.
Мне просто интересно, есть ли более Pythonic способ сделать это?
Ответы
Ответ 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
.