Python: выберите случайную строку из файла, затем удалите эту строку
Я новичок в Python (в том, что я изучил его через курс CodeAcademy) и мог бы помочь с определением этого.
У меня есть файл 'TestingDeleteLines.txt', который содержит около 300 строк текста. Прямо сейчас я пытаюсь заставить его напечатать мне 10 случайных строк из этого файла, а затем удалить эти строки.
Так что, если мой файл имеет 10 строк:
Carrot
Banana
Strawberry
Canteloupe
Blueberry
Snacks
Apple
Raspberry
Papaya
Watermelon
Мне нужно, чтобы он случайно выбрал эти строки, сказал мне, что они случайно выбрали чернику, морковь, арбуз и банан, а затем удалил эти строки.
Проблема в том, что когда Python читает файл, он читает этот файл, и как только он доходит до конца, он не возвращается и не удаляет строки. В настоящее время я думал, что могу написать строки в список, затем снова открыть файл, сопоставить список с текстовым файлом и, если он найдет совпадение, удалить строки.
Моя текущая проблема состоит из двух частей:
- Это дублирование случайных элементов. Если он выбирает линию, мне нужно, чтобы она не выбиралась снова. Однако использование
random.sample
, похоже, не работает, так как мне нужно, чтобы эти строки выделялись, когда я позже использую каждую строку для добавления в URL. -
Я не чувствую, что моя логика (написать в array-> найти совпадения в тексте file-> удалить) - самая идеальная логика. Есть ли лучший способ написать это?
import webbrowser
import random
"""url= 'http://www.google.com'
webbrowser.open_new_tab(url+myline)""" Eventually, I need a base URL + my 10 random lines opening in each new tab
def ShowMeTheRandoms():
x=1
DeleteList= []
lines=open('TestingDeleteLines.txt').read().splitlines()
for x in range(0,10):
myline=random.choice(lines)
print(myline) """debugging, remove later"""
DeleteList.append(myline)
x=x+1
print DeleteList """debugging, remove later"""
ShowMeTheRandoms()
Ответы
Ответ 1
У меня есть файл TestingDeleteLines.txt, который содержит около 300 строк текста. Прямо сейчас, я пытаюсь заставить его напечатать мне 10 случайных строк из этого файла, а затем удалить эти строки.
#!/usr/bin/env python
import random
k = 10
filename = 'TestingDeleteLines.txt'
with open(filename) as file:
lines = file.read().splitlines()
if len(lines) > k:
random_lines = random.sample(lines, k)
print("\n".join(random_lines)) # print random lines
with open(filename, 'w') as output_file:
output_file.writelines(line + "\n"
for line in lines if line not in random_lines)
elif lines: # file is too small
print("\n".join(lines)) # print all lines
with open(filename, 'wb', 0): # empty the file
pass
Это алгоритм O(n**2)
, который может быть улучшен, если это необходимо (вам это не нужно для крошечного файла, такого как ваш ввод)
Ответ 2
Точка: вы не "удаляете" из файла, а переписываете весь файл (или другой) с новым контентом. Канонический способ состоит в том, чтобы прочитать исходный файл по строкам, записать строки, которые вы хотите сохранить во временный файл, а затем заменить старый файл на новый.
with open("/path/to/source.txt") as src, open("/path/to/temp.txt", "w") as dest:
for line in src:
if should_we_keep_this_line(line):
dest.write(line)
os.rename("/path/to/temp.txt", "/path/to/source.txt")
Ответ 3
Как насчет list.pop - он дает вам элемент и обновляет список за один шаг.
lines = readlines()
deleted = []
indices_to_delete = random.sample(xrange(len(lines)), 10)
# sort to delete biggest index first
indices_to_delete.sort(reverse=True)
for i in indices_to_delete:
# lines.pop(i) delete item at index i and return the item
# do you need it or its index in the original file than
deleted.append((i, lines.pop(i)))
# write the updated *lines* back to the file or new file ?!
# and you have everything in deleted if you need it again
Ответ 4
Предположим, что у вас есть список строк из вашего файла, хранящихся в items
>>> items = ['a', 'b', 'c', 'd', 'e', 'f']
>>> choices = random.sample(items, 2) # select 2 items
>>> choices # here are the two
['b', 'c']
>>> for i in choices:
... items.remove(i)
...
>>> items # tee daa, no more b or c
['a', 'd', 'e', 'f']
Здесь вы должны перезаписать свой предыдущий текстовый файл с содержимым items
, соединяющим с вашей предпочтительной строкой \r\n или\n. readlines()
не разделяет концы строк, поэтому, если вы используете этот метод, вам не нужно добавлять свои собственные окончания строки.
Ответ 5
Чтобы выбрать случайную строку из файла, вы можете использовать пространственный эффективный однопроходный алгоритм выборки коллектора. Чтобы удалить эту строку, вы можете распечатать все, кроме выбранной строки:
#!/usr/bin/env python3
import fileinput
with open(filename) as file:
k = select_random_it(enumerate(file), default=[-1])[0]
if k >= 0: # file is not empty
with fileinput.FileInput(filename, inplace=True, backup='.bak') as file:
for i, line in enumerate(file):
if i != k: # keep line
print(line, end='') # stdout is redirected to filename
где select_random_it()
реализует алгоритм выборки коллектора:
import random
def select_random_it(iterator, default=None, randrange=random.randrange):
"""Return a random element from iterator.
Return default if iterator is empty.
iterator is exhausted.
O(n)-time, O(1)-space algorithm.
"""
# from https://stackoverflow.com/a/1456750/4279
# select 1st item with probability 100% (if input is one item, return it)
# select 2nd item with probability 50% (or 50% the selection stays the 1st)
# select 3rd item with probability 33.(3)%
# select nth item with probability 1/n
selection = default
for i, item in enumerate(iterator, start=1):
if randrange(i) == 0: # random [0..i)
selection = item
return selection
Чтобы напечатать k
случайные строки из файла и удалить их:
#!/usr/bin/env python3
import random
import sys
k = 10
filename = 'TestingDeleteLines.txt'
with open(filename) as file:
random_lines = reservoir_sample(file, k) # get k random lines
if not random_lines: # file is empty
sys.exit() # do nothing, exit immediately
print("\n".join(map(str.strip, random_lines))) # print random lines
delete_lines(filename, random_lines) # delete them from the file
где reservoir_sample()
использует тот же алгоритм, что и select_random_it()
, но позволяет выбирать элементы k
вместо одного:
import random
def reservoir_sample(iterable, k,
randrange=random.randrange, shuffle=random.shuffle):
"""Select *k* random elements from *iterable*.
Use O(n) Algorithm R https://en.wikipedia.org/wiki/Reservoir_sampling
If number of items less then *k* then return all items in random order.
"""
it = iter(iterable)
if not (k > 0):
raise ValueError("sample size must be positive")
sample = list(islice(it, k)) # fill the reservoir
shuffle(sample)
for i, item in enumerate(it, start=k+1):
j = randrange(i) # random [0..i)
if j < k:
sample[j] = item # replace item with gradually decreasing probability
return sample
и delete_lines()
функция утилиты удаляет выбранные случайные строки из файла:
import fileinput
import os
def delete_lines(filename, lines):
"""Delete *lines* from *filename*."""
lines = set(lines) # for amortized O(1) lookup
with fileinput.FileInput(filename, inplace=True, backup='.bak') as file:
for line in file:
if line not in lines:
print(line, end='')
os.unlink(filename + '.bak') # remove backup if there is no exception
reservoir_sample()
, delete_lines()
funciton не загружает весь файл в память, и поэтому они могут работать для произвольных больших файлов.
Ответ 6
Возможно, вы могли бы попробовать создать 10 случайных чисел от 0 до 300, используя
deleteLineNums = random.sample(xrange(len(lines)), 10)
а затем удалите из массива строк, сделав копию со списком:
linesCopy = [line for idx, line in enumerate(lines) if idx not in deleteLineNums]
lines[:] = linesCopy
И затем верните строки в "TestingDeleteLines.txt".
Чтобы узнать, почему работает код копирования выше, это сообщение может быть полезно:
Удалить элементы из списка во время итерации
РЕДАКТИРОВАТЬ. Чтобы получить строки с произвольно создаваемыми индексами, просто выполните:
actualLines = []
for n in deleteLineNums:
actualLines.append(lines[n])
Затем actualLines связывает фактический текст строки случайно генерируемых индексов строк.
РЕДАКТИРОВАТЬ: Или даже лучше, используйте понимание списка:
actualLines = [lines[n] for n in deleteLineNums]