2015-09-25 2 views
2

Я новичок в Python (в этом я узнал его по курсу CodeAcademy) и мог бы использовать некоторую помощь, чтобы понять это.Python: выберите случайную строку из файла, затем удалите эту строку.

У меня есть файл «TestingDeleteLines.txt», это около 300 строк текста. Прямо сейчас, я пытаюсь заставить его напечатать мне 10 случайных строк из этого файла, а затем удалить эти строки.

Так что, если мой файл имеет 10 строк:

Морковь

Banana

Клубничный

Canteloupe

Blueberry

Закуски

Яблоко

малина

Papaya

Арбуз

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

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

Моя нынешняя проблема имеет два аспекта:

  1. Это дублирование случайных элементов. Если он выбирает линию, мне нужно, чтобы она не выбирала ту же линию снова. Однако использование random.sample, похоже, не работает, так как мне нужны эти строки, отделенные, когда я позже использую каждую строку для добавления к URL-адресу.
  2. Я не чувствую, что моя логика (запись в массив-> поиск совпадений в текстовом файле-> удаление) является самой идеальной логикой. Есть ли лучший способ написать это?

    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() 
    
+3

Способ сделать это, чтобы открыть файл, прочитать во всех строках с помощью 'readlines()', закрыть файл, а затем переписать весь файл. –

+0

Как мне сказать, чтобы просто удалить случайные строки? –

+0

['' 'file_object.seek (0)' ''] (https://docs.python.org/3/library/io.html#io.TextIOBase.seek) должно позволить вам снова начать повторение с начала. В вашем примере '' 'lines''' выглядит так: * file_object *. – wwii

ответ

1

I have a file, 'TestingDeleteLines.txt', that's about 300 lines of text. Right now, I'm trying to get it to print me 10 random lines from that file, then delete those lines.

#!/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) алгоритма, который can be improved при необходимости (не нужно для небольшого файла такого как ваш вход)

+0

Как начинающий кодер, это было очень легко читать и понимать, поэтому я очень благодарю вас. :) Теперь проблема заключается в том, что она бросает синтаксическую ошибку в линии elif, если я помещаю ее в функцию. Есть ли у вас какие-либо идеи относительно того, почему это может быть? –

+0

@SamW: Мое предположение, вы нарушили отступ кода (убедитесь, что вы не смешиваете закладки и пробелы для отступов, используйте либо, либо не оба), но я не могу быть уверен, что вы не показываете * точное * code: [создать минимальный, но полный пример кода] (http://stackoverflow.com/help/mcve), который демонстрирует проблему и добавляет ее в ваш вопрос (или спрашивает новую, если вы считаете, что ошибка может быть интересной для кого-то еще). – jfs

+1

О, боже, ду! Большое вам спасибо, это было безумно полезно, и я многому научился. :) Я действительно искренне ценю, что вы нашли время, чтобы написать об этом. –

3

Пойнт: вы не «удалить» из файла, но переписать весь файл (или другой) с новым содержанием. Канонический способ - прочитать исходный файл по строкам, записать строки, которые вы хотите сохранить во временный файл, а затем заменить старый файл на новый.

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") 
+0

Итак, вместо того, чтобы писать случайные строки в массив, я должен написать все другие неслучайные строки в массив и создать новый файл? –

+0

Зачем использовать массив (FWIW в Python это 'list' not' array') вообще? Прочитайте строку из источника, решите, хотите ли вы сохранить ее, если да, напишите ее в файл temp, повторите попытку rince. –

1

А как насчет 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 
+0

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

+0

@SamW Удаленные строки находятся в переменной 'deleted', а остальные строки все еще находятся в' lines'. Что еще тебе нужно? –

+0

Почему вам нужно сортировать индексы здесь? ('line.pop (i)' is 'O (n)' в любом случае) – jfs

1

Давайте предположим, что у вас есть список строк из файла, хранящихся в 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 вступающих предпочитаемых строка, заканчивающаяся \ г \ n или \ n. readlines() не разбивает концы строк, поэтому, если вы используете этот метод, вам не нужно добавлять свои собственные окончания строк.

+0

«Присоединение к вашей предпочтительной строке end \ r \ n или \ n "неверно, потому что элементы списка строк readlines содержат новую строку в конце ... это добавит дополнительные пустые строки – rebeling

+0

@ rebeling с моей стороны. Я отредактирую соответственно. –

0

Может быть, вы могли бы попробовать генерировать 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'.

Чтобы понять, почему скопировать код выше работы, этот пост может быть полезным:

Remove items from a list while iterating

EDIT: Для того, чтобы получить строки в случайно сгенерированных индексов, просто сделать:

actualLines = [] 
for n in deleteLineNums: 
    actualLines.append(lines[n]) 

Затем actualLines передает фактический текст строки случайно сгенерированных строк.

EDIT: Или еще лучше, использовать список comrehension:

actualLines = [lines[n] for n in deleteLineNums] 
+0

Как я могу подключить это к моим оригинальным случайным строкам? 'для й в диапазоне (0,10): \t \t myline = random.choice (линия) \t \t печати (myline)' Так что вытаскивает "морковь, банан, яблоко". Я хочу удалить те же самые строки. Если я добавлю deleteLineNums = random.sample (xrange (len (lines)), 10), это просто дает мне список чисел, но эти числа не соответствуют случайным строкам, которые я уже вытащил. Я что-то не понимаю? –

+0

Итак, в этом случае вы будете идентифицировать случайные индексы строк для удаления, а не сами строки. Обратите внимание: поскольку в обоих случаях строки выбираются случайным образом, оба подхода эквивалентны при идентификации 10 случайных строк из файла.EDIT: (Таким образом, вы будете делать это вместо выбора фактических строк в случайном порядке, и эта замена дает вам логически тот же результат). Имеет ли это смысл? – sgrg

+0

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

1

Чтобы выбрать случайную строку из файла, вы можете использовать пространственный эффективный однопроходный reservoir-sampling algorithm.Для того, чтобы удалить эту строку, можно напечатать все, кроме выбранной линии:

#!/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() implements the reservoir-sampling algorithm:

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 не загружать весь файл в память и поэтому они могут работать для произвольных больших файлов.

Смежные вопросы