Как мне прочитать случайную строку из одного файла в Python?

31

Есть ли встроенный метод? Если нет, то как я могу это сделать, не затрачивая слишком много накладных расходов?

  • 0
    @Greg Это Perl, а не Python
  • 2
    @quantumSoup: Вопрос использует Perl в своих примерах, но вопрос не зависит от языка. Самые полезные ответы используют псевдокод, легко переводимый на ваш язык.
Показать ещё 3 комментария
Теги:

10 ответов

58
Лучший ответ

Не встроенный, но алгоритм R(3.4.2) (Waterman "Reservoir Algorithm") от Knuth "Искусство компьютерного программирования" хорош (в очень упрощенной версии):

import random

def random_line(afile):
    line = next(afile)
    for num, aline in enumerate(afile, 2):
      if random.randrange(num): continue
      line = aline
    return line

Итератор num,... in enumerate(..., 2) создает последовательность 2, 3, 4... Следовательно, randrange будет равен 0 с вероятностью 1.0/num - и той вероятностью, с которой мы должны заменить текущую выбранную строку (особый случай с размером выборки 1 ссылочного алгоритма - см. книгу Кнута для доказательства правильности == и, конечно, мы также имеем дело с достаточно маленьким "резервуаром", чтобы уместиться в память; -))... и именно та вероятность, с которой мы это делаем.

  • 1
    +1 за перевод с MIX на python
  • 2
    Это пробоотбор, верно?
Показать ещё 4 комментария
26
import random
lines = open('file.txt').read().splitlines()
myline =random.choice(lines)
print(myline)

Для очень длинного файла: искать случайное место в файле на основе его длины и находить два символа новой строки после позиции (или новой строки и конца файла). Повторите 100 символов до или с начала файла, если исходная позиция поиска была < 100, если мы оказались внутри последней строки.

Однако это сложнее, так как файл является итератором. Так что сделайте список и возьмите random.choice(если вам нужно много, используйте random.sample):

import random
print(random.choice(list(open('file.txt'))))
  • 12
    Если задача состоит в том, чтобы прочитать только строку, нет смысла загружать полный файл в память.
8

Это зависит от того, что вы подразумеваете под "слишком большими" накладными расходами. Если хранить весь файл в памяти, возможно, что-то вроде

import random

random_lines = random.choice(open("file").readlines())

сделал бы трюк.

7

Хотя я опаздываю на четыре года, я думаю, что у меня есть самое быстрое решение. Недавно я написал пакет python под названием linereader, который позволяет вам манипулировать указателями дескрипторов файлов.

Вот простое решение для получения случайной строки с этим пакетом:

from random import randint
from linereader import dopen

length = #lines in file
filename = #directory of file

file = dopen(filename)
random_line = file.getline(randint(1, length))

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

Если ваш файл очень мал (достаточно мал, чтобы вписаться в MB), вы можете заменить dopen на copen и сделать кэшированную запись файла в памяти. Это происходит не только быстрее, но вы получаете количество строк в файле, поскольку оно загружается в память; это делается для вас. Все, что вам нужно сделать, это создать случайный номер строки. Вот пример кода для этого.

from random import randint
from linereader import copen

file = copen(filename)
lines = file.count('\n')
random_line = file.getline(randint(1, lines))

Я просто очень рад, потому что увидел кого-то, кто мог бы воспользоваться моим пакетом! Извините за мертвый ответ, но пакет определенно может быть применен ко многим другим проблемам.

  • 0
    У меня была строка ValueError нет. не найдено, но строка отсутствует. был меньше, чем размер файла.
2

Если вы не хотите читать весь файл, вы можете искать его в середине файла, затем искать назад для новой строки и называть readline.

Вот Python3 script, который делает именно это,

Один из недостатков этого метода заключается в том, что короткие линии имеют меньшее вероятность появления.

def read_random_line(f, chunk_size=16):
    import os
    import random
    with open(f, 'rb') as f_handle:
        f_handle.seek(0, os.SEEK_END)
        size = f_handle.tell()
        i = random.randint(0, size)
        while True:
            i -= chunk_size
            if i < 0:
                chunk_size += i
                i = 0
            f_handle.seek(i, os.SEEK_SET)
            chunk = f_handle.read(chunk_size)
            i_newline = chunk.rfind(b'\n')
            if i_newline != -1:
                i += i_newline + 1
                break
            if i == 0:
                break
        f_handle.seek(i, os.SEEK_SET)
        return f_handle.readline()
1
import random

with open("file.txt", "r") as f:
    lines = f.readlines()
    print (random.choice(lines))
1

Ищите случайную позицию, читайте строку и отбрасывайте ее, затем читайте другую строку. Распределение линий не будет нормальным, но это не всегда имеет значение.

  • 3
    В частности, это делает невозможным выделение первой строки (а также выбор других строк с вероятностью, пропорциональной длине каждой предыдущей строки). Мой А также не производит нормальное распределение (это было бы странно - что значит, что дисперсия ?!), но равномерное, которое, кажется, несколько более вероятно соответствует значению ОП для «случайного».
  • 0
    Чтобы преодолеть проблему, указанную @AlexMartelli, выберите первую строку на случай, если случайный поиск приведет вас к последней строке. Но другая проблема здесь заключается в том, что строка, имеющая относительно больше слов для других строк, будет иметь более высокую вероятность выбора.
0

Немного улучшенная версия ответа Алекса Мартелли, которая обрабатывает пустые файлы (возвращает значение по default):

def random_line(afile, default=None):
    line = default
    for i, aline in enumerate(afile, start=1):
        if randrange(i) == 0:  # random int [0..i)
            line = aline
    return line

Этот подход можно использовать для получения случайного элемента из любого итератора, использующего время O(n) и пространство O(1).

0

Это может быть громоздким, но он работает, я думаю? (по крайней мере, для файлов txt)

import random
choicefile=open("yourfile.txt","r")
linelist=[]
for line in choicefile:
    linelist.append(line)
choice=random.choice(linelist)
print(choice)

Он считывает каждую строку файла и добавляет его в список. Затем он выбирает случайную строку из списка. Если вы хотите удалить строку после ее выбора, просто сделайте

linelist.remove(choice)

Надеюсь, это может помочь, но, по крайней мере, никаких дополнительных модулей и импорта (кроме случайных) и относительно легкого.

0

Вы можете добавить строки в набор(), который будет произвольно менять порядок.

filename=open("lines.txt",'r')
f=set(filename.readlines())
filename.close()

Чтобы найти 1-ю строку:

print(next(iter(f)))

Чтобы найти третью строку:

print(list(f)[2])

Чтобы перечислить все строки в наборе:

for line in f:
    print(line)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню