Writelines пише рядки без нового рядка, просто заповнює файл


77

У мене є програма, яка записує список у файл. Список - це список рядків, розділених трубопроводами, і вони повинні бути записані у файл таким чином:

123|GSV|Weather_Mean|hello|joe|43.45
122|GEV|temp_Mean|hello|joe|23.45
124|GSI|Weather_Mean|hello|Mike|47.45

АЛЕ він написав їм такий рядок аххххх:

123|GSV|Weather_Mean|hello|joe|43.45122|GEV|temp_Mean|hello|joe|23.45124|GSI|Weather_Mean|hello|Mike|47.45

Ця програма записала всі рядки в один рядок без будь-яких розривів. Це мені дуже боляче, і я повинен зрозуміти, як це змінити, але в будь-якому випадку, де моя програма тут неправильна? Я думав, що рядки для запису повинні писати рядки у файлі, а не просто писати все в один рядок ..

fr = open(sys.argv[1], 'r') # source file
fw = open(sys.argv[2]+"/masked_"+sys.argv[1], 'w') # Target Directory Location

for line in fr:
    line = line.strip()
    if line == "":
        continue
    columns = line.strip().split('|')
    if columns[0].find("@") > 1:
        looking_for = columns[0] # this is what we need to search
    else:
        looking_for = "Dummy@dummy.com"
    if looking_for in d:
        # by default, iterating over a dictionary will return keys
            new_line = d[looking_for]+'|'+'|'.join(columns[1:])
            line_list.append(new_line)
    else:
        new_idx = str(len(d)+1)
        d[looking_for] = new_idx
        kv = open(sys.argv[3], 'a')
        kv.write(looking_for+" "+new_idx+'\n')
        kv.close()
        new_line = d[looking_for]+'|'+'|'.join(columns[1:])
        line_list.append(new_line)
fw.writelines(line_list)

Здається, ти "\n"ніде не додаєш у своєму line_list.
Rohit Jain

2
Ви можете поглянути на csv
esauro

Де я можу додати "\ n"? Чи додати його до об'єкта списку ??
user836087

Відповіді:


59

Це насправді досить поширена проблема для новачків Python - тим більше, що у стандартній бібліотеці та популярних сторонніх бібліотеках деякі функції читання вилучають нові рядки, але майже жодні функції написання (крім logпов'язаних матеріалів) не додають їх.

Отже, існує багато коду Python, який робить такі речі:

fw.write('\n'.join(line_list) + '\n')

або

fw.write(line + '\n' for line in line_list)

Будь-який з них правильний, і, звичайно, ви навіть можете написати власну функцію writelinesWithNewlines, яка обгортає це ...

Але робити це слід лише в тому випадку, якщо ви не можете цього уникнути.

Краще, якщо ви зможете створити / зберегти нові рядки в першу чергу - як у пропозиціях Грега Хьюгілла:

line_list.append(new_line + "\n")

І навіть краще, якщо ви зможете працювати на вищому рівні, ніж необроблені рядки тексту, наприклад, використовуючи модуль csv у стандартній бібліотеці, як пропонує esuaro.

Наприклад, одразу після визначення fwви можете зробити наступне:

cw = csv.writer(fw, delimiter='|')

Тоді замість цього:

new_line = d[looking_for]+'|'+'|'.join(columns[1:])
line_list.append(new_line)

Ви робите це:

row_list.append(d[looking_for] + columns[1:])

І в кінці, замість цього:

fw.writelines(line_list)

Ви робите це:

cw.writerows(row_list)

Нарешті, ваш дизайн - "відкрити файл, потім скласти список рядків, які потрібно додати до файлу, і записати їх усі відразу". Якщо ви збираєтеся відкрити файл зверху, чому б просто не написати рядки по одному? Незалежно від того, чи використовуєте ви прості записи або a csv.writer, це спростить ваше життя, а код стане легшим для читання. (Іноді можуть бути причини простоти, ефективності чи правильності написання файлу відразу, але коли ви перейшли openповністю на протилежний кінець програми від програми write, ви майже втратили будь-які переваги всіх -якось.)


46
все це тому, що python не робить очевидного зwritelines
StephenBoesch

33
Ого. Це смішна поведінка з боку Python: readlines () повинен вилучати нові рядки, але ні, а writelines () повинен додавати нові рядки, але ні. Весь сенс у тому, щоб мати лінії зчитування та написання, полягає в тому, що ви можете легко читати та писати списки. Легко обурює!
Джеймс

6
Чи варто такий PEP, як f.writelines (lines, end = '\ n')?
John Lin

має бути fw.writelines (лінія + \ п 'для рядка в line_list) у другому прикладі
дан

46

Документація дляwritelines() держав:

writelines() не додає роздільників

Тож вам потрібно буде додати їх самостійно. Наприклад:

    line_list.append(new_line + "\n")

щоразу, коли ви додаєте новий елемент до line_list.


+1. Це найпростіше рішення, і майже напевно найдоречніше, якщо ОП не хоче переходити на щось на кшталт вищого рівня csv.
abarnert

86
Яка жахлива назва функції просто задля "узгодження" ліній читання.
Сенді Чепмен

25
гірше, ніж жахлива назва: жахлива функціональність
StephenBoesch

25

Як зазначали інші, writelinesце неправильна назва (вона смішно не додає нові рядки в кінці кожного рядка).

Для цього явно додайте його в кожен рядок:

with open(dst_filename, 'w') as f:
    f.writelines(s + '\n' for s in lines)

8

writelines()не додає роздільників. Ви можете змінити список рядків за допомогою, map()щоб додати новий \n(розрив рядка) в кінці кожного рядка.

items = ['abc', '123', '!@#']
items = map(lambda x: x + '\n', items)
w.writelines(items)

2

Як зазначали інші, всупереч тому, що означатиме назва методу, writelinesне додає роздільників рядків. Це футляр для підручника для генератора. Ось надуманий приклад:

def item_generator(things):
    for item in things:
        yield item
        yield '\n'

def write_things_to_file(things):
    with open('path_to_file.txt', 'wb') as f:
        f.writelines(item_generator(things))

Переваги: ​​додає нові рядки явно, не змінюючи вхідних чи вихідних значень або виконуючи будь-яку брудну конкатенацію рядків. І, що важливо, не створює ніяких нових структур даних в пам'яті. IO (запис у файл) - це коли подібні речі, як правило, мають значення. Сподіваюся, це комусь допомагає!


1

Як ми добре встановили тут, writelines не додає нових рядків для вас. Але те, чого, здається, усім не вистачає, - це те, що це не обов’язково, коли використовується як прямий «аналог», readlines()і початкове прочитання наполегливо зберігало нові рядки!

Коли ви відкриваєте файл для читання у двійковому режимі (через'rb' ), а потім використовуйте readlines()для отримання вмісту файлу в пам'ять, розділену за рядками, нові рядки залишаються прикріпленими до кінця ваших рядків! Отже, якщо ви згодом перепишете їх, ви, швидше за все, не хочете writelinesнічого додавати!

Отже, якщо ви робите щось на зразок:

with open('test.txt','rb') as f: lines=f.readlines()
with open('test.txt','wb') as f: f.writelines(lines)

У вас повинен вийти той самий вміст файлу, з яким ви почали.


І коли саме ви хочете прочитати файл, щоб просто записати його назад?
рядовий

1
Ви сприймаєте приклад коду занадто буквально. Я не включив помилковий контекст та сторонній приклад коду, коли ви читаєте файл і записуєте його назад. Між нормальним явищем було б щось середнє, де ви могли б модифікувати вміст. Або, можливо, ви "копіюєте" / налаштовуєте вихідний файл у файл призначення. Справа в тому, що аналоги readlines / writelines аналізують цю проблему неявно.
BuvinJ

Вибачте, я не хотів наполягати на цьому питанні. Просто більшість випадків, коли я читаю файл і .stripзаписую назад у нього чи в інший файл, одним із методів, який я називаю, є або '.rstrip () `, тож у моєму робочому процесі це ніколи не відбудеться.
рядовий

Np. Я часто робив те саме, але якщо ви потрапили на цю сторінку, можливо, ви захочете змінити цю практику в деяких випадках використання, коли ви хочете зберегти закінчення рядків, ви можете уникати їх явного додавання, маючи на увазі цю публікацію .
BuvinJ

0

Оскільки ми хочемо відокремлювати лише рядки, а writelinesфункція в python не підтримує додавання роздільника між рядками, я написав простий код, який найкраще відповідає цій проблемі:

sep = "\n" # defining the separator
new_lines = sep.join(lines) # lines as an iterator containing line strings

і, нарешті:

with open("file_name", 'w') as file:
    file.writelines(new_lines)

і готово.


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

Немає сенсу називати лінії запису, якщо ви заздалегідь приєднали рядки до одного рядка.
Далібор Філус
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.