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

Скажем, файл слишком большой, чтобы его можно было записать в память. Как я могу получить от него случайную строку? Спасибо.

Обновление: Я хочу, чтобы вероятность того, что каждая строка будет равна.

Ответ 1

Вот решение. Взгляните на метод select(), который делает реальную вещь (метод main() многократно выполняет функцию select(), чтобы показать, что распределение действительно довольно равномерное).

Идея проста: когда вы читаете первую строчку, у нее есть 100% шанс быть выбранным в качестве результата. Когда вы читаете вторую строчку, у нее есть 50% шанс заменить первую строку в качестве результата. Когда вы читаете 3-ю строчку, у нее есть 33% шанс стать результатом. Четвертая строка имеет 25% и т.д....

import java.io.*;
import java.util.*;

public class B {

  public static void main(String[] args) throws FileNotFoundException {
     Map<String,Integer> map = new HashMap<String,Integer>();
     for(int i = 0; i < 1000; ++i)
     {
        String s = choose(new File("g:/temp/a.txt"));
        if(!map.containsKey(s))
           map.put(s, 0);
        map.put(s, map.get(s) + 1);
     }

     System.out.println(map);
  }

  public static String choose(File f) throws FileNotFoundException
  {
     String result = null;
     Random rand = new Random();
     int n = 0;
     for(Scanner sc = new Scanner(f); sc.hasNext(); )
     {
        ++n;
        String line = sc.nextLine();
        if(rand.nextInt(n) == 0)
           result = line;         
     }

     return result;      
  }
}

Ответ 2

Чтение всего файла, если вы хотите, чтобы только одна строка казалась немного чрезмерной. Следующее должно быть более эффективным:

  • Используйте RandomAccessFile для поиска случайной позиции байта в файле.
  • Искать влево и вправо до следующего терминатора линии. Пусть L - линия между ними.
  • С вероятностью (MIN_LINE_LENGTH/L.length) верните L. В противном случае начните с шага 1.

Это вариант выборки отклонения.

Длина линии включает символ (-ы) терминатора линии, поэтому MIN_LINE_LENGTH >= 1. (Все лучше, если вы знаете более жесткую привязку длины строки).

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

Ответ 3

Либо вы

  • дважды читайте файл - один раз, чтобы подсчитать количество строк, второй раз, чтобы извлечь случайную строку, или

  • использовать выборки коллектора

Ответ 4

Оглядываясь на ответ Itay, похоже, что он читает файл тысячу раз после выборки одной строки кода, тогда как истинная выборка коллектора должна переходить только через "ленту" один раз. Я разработал код для перебора кода один раз с реальной выборкой коллектора на основе этого и различных описаний в Интернете.

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;

public class reservoirSampling {

    public static void main(String[] args) throws FileNotFoundException, IOException{
        Sampler mySampler = new Sampler();
        List<String> myList = mySampler.sampler(10);
        for(int index = 0;index<myList.size();index++){
            System.out.println(myList.get(index));
        }
    }
}

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Scanner;

public class Sampler {

    public Sampler(){}
    public List<String> sampler (int reservoirSize) throws FileNotFoundException, IOException
    {
        String currentLine=null;
        //reservoirList is where our selected lines stored
        List <String> reservoirList= new ArrayList<String>(reservoirSize); 
        // we will use this counter to count the current line number while iterating
        int count=0; 

        Random ra = new Random();
        int randomNumber = 0;
        Scanner sc = new Scanner(new File("Open_source.html")).useDelimiter("\n");
        while (sc.hasNext())
        {
            currentLine = sc.next();
            count ++;
            if (count<=reservoirSize)
            {
                reservoirList.add(currentLine);
            }
            else if ((randomNumber = (int) ra.nextInt(count))<reservoirSize)
            {
                reservoirList.set(randomNumber, currentLine);
            }
        }
        return reservoirList;
    }
}

Основная предпосылка заключается в том, что вы заполняете резервуар, а затем возвращаетесь к нему и заполняете случайные строки шансом 1/ReservoirSize. Надеюсь, это обеспечит более эффективный код. Пожалуйста, дайте мне знать, если это не сработает для вас, поскольку я буквально выбил его через полчаса.

Ответ 5

Использовать RandomAccessFile:

  1. Построить файл RandomAccessFile, файл
  2. Получите длину этого файла, filelen, вызывая file.length()
  3. Создайте случайное число, pos, между 0 и filelen
  4. Вызовите файл.seek(pos) для поиска случайной позиции
  5. Вызовите файл.readLine(), чтобы перейти в конец текущей строки
  6. Прочитайте следующую строку, снова вызвав файл.readLine()

Используя этот метод, я случайно выбрал строки из Brown Corpus и могу легко получить 1000 случайных выборок из случайно выбранных файлов за несколько секунд. Если бы я попытался сделать то же самое, прочитав каждый файл по очереди, мне потребовалось бы гораздо больше времени.

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

Ответ 6

Используйте BufferedReader и прочитайте строку. Используйте объект java.util.Random для случайного остановки;)