Python: elige una línea aleatoria del archivo, luego borra esa línea

Soy nuevo en Python (porque lo aprendí a través de un curso de CodeAcademy) y podría necesitar ayuda para resolverlo.

Tengo un archivo, ‘TestingDeleteLines.txt’, que tiene aproximadamente 300 líneas de texto. En este momento, estoy intentando que me imprima 10 líneas aleatorias de ese archivo y luego elimine esas líneas.

Así que si mi archivo tiene 10 líneas:

Carrot Banana Strawberry Canteloupe Blueberry Snacks Apple Raspberry Papaya Watermelon 

Lo necesito para seleccionar aleatoriamente esas líneas, decirme que se seleccionó al azar arándano, zanahoria, sandía y plátano, y luego eliminar esas líneas.

El problema es que cuando Python lee un archivo, lee ese archivo y una vez que llega al final, no regresará y eliminará las líneas. Mi idea actual era que podía escribir las líneas en una lista, luego volver a abrir el archivo, hacer coincidir la lista con el archivo de texto y, si encuentra una coincidencia, eliminar las líneas.

Mi problema actual es doble:

  1. Está duplicando los elementos aleatorios. Si elige una línea, la necesito para no volver a elegir esa misma línea. Sin embargo, el uso de random.sample no parece funcionar, ya que necesito separar esas líneas cuando luego use cada línea para adjuntarlas a una URL.
  2. No siento que mi lógica (escribir en matriz-> buscar coincidencias en el archivo de texto-> eliminar) sea la lógica más ideal. ¿Hay una mejor manera de escribir esto?

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

Tengo un archivo, ‘TestingDeleteLines.txt’, que tiene aproximadamente 300 líneas de texto. En este momento, estoy intentando que me imprima 10 líneas aleatorias de ese archivo y luego elimine esas líneas.

 #!/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 

Es un algoritmo O(n**2) que puede mejorarse si es necesario (no lo necesita para un archivo pequeño como su entrada)

El punto es: no se “elimina” de un archivo, pero se vuelve a escribir todo el archivo (u otro) con contenido nuevo. La forma canónica es leer el archivo original línea por línea, volver a escribir las líneas que desea conservar en un archivo temporal y luego reemplazar el archivo antiguo por el nuevo.

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

¿Qué pasa con list.pop? Te da el artículo y actualiza la lista en un solo paso.

 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 

Para elegir una línea aleatoria de un archivo, puede utilizar un algoritmo de muestreo de yacimientos de una sola pasada que ahorre espacio. Para eliminar esa línea, puede imprimir todo excepto la línea elegida:

 #!/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 

donde select_random_it() implementa el algoritmo de muestreo de reservorio :

 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 

Para imprimir k líneas al azar de un archivo y eliminarlas:

 #!/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 

donde reservoir_sample() usa el mismo algoritmo que select_random_it() pero permite elegir k elementos en lugar de uno:

 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 

y la función de utilidad delete_lines() elimina las líneas aleatorias seleccionadas del archivo:

 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() reservoir_sample() , delete_lines() no cargan todo el archivo en la memoria y, por lo tanto, pueden funcionar para archivos grandes arbitrarios.

Supongamos que tiene una lista de líneas de su archivo almacenadas en 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'] 

Desde aquí, sobrescribiría su archivo de texto anterior con el contenido de los items unen a su línea preferida que termina con \ r \ n o \ n. readlines() no elimina los finales de línea, por lo que si usa ese método, no necesita agregar sus propios finales de línea.

Tal vez podrías intentar generar 10 números aleatorios de 0 a 300 usando

 deleteLineNums = random.sample(xrange(len(lines)), 10) 

y luego elimínelo de la matriz de líneas haciendo una copia con la lista de comprensión:

 linesCopy = [line for idx, line in enumerate(lines) if idx not in deleteLineNums] lines[:] = linesCopy 

Y luego, escribiendo líneas de nuevo en ‘TestingDeleteLines.txt’.

Para ver por qué funciona el código de copia anterior, esta publicación puede ser útil:

Eliminar elementos de una lista mientras se está iterando

EDITAR : Para obtener las líneas en los índices generados aleatoriamente, simplemente haga:

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

A continuación, las líneas reales contienen el texto de línea real de los índices de línea generados aleatoriamente.

EDIT: O incluso mejor, utilice una lista de comprensión:

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