【问题标题】:Randomly select elements from a big text file从大文本文件中随机选择元素
【发布时间】:2013-06-20 00:16:04
【问题描述】:

我有一个包含 3000 万个用户 ID 的大文件。那个大文件看起来像这样,每一行都有一个用户 ID。

149905320
1165665384
66969324
886633368
1145241312
286585320
1008665352
1135545396
186217320
132577356

现在,我打算从那个大文本文件中获取任意一行。我知道那个大文本文件中用户 ID 的总数。我不确定从那个大文本文件中选择随机元素的最佳方法是什么。我正在考虑将所有这 3000 万个用户 ID 存储在一个集合中,然后从 hastset 中随机选择元素,但使用这种方法会出现内存不足错误。

这就是原因,我试图从一个大文本文件中随机选择元素。

final String id = generateRandomUserId(random);

/**
 * Select random elements from the a big text file
 * 
 * @param userIdsSet2
 * @param r
 * @return
 */
private String generateRandomUserId(Random r) {

     File bigFile = new File("C:\\bigfile.txt");

     //randomly select elements from a big text file         


}

最好的方法是什么?

【问题讨论】:

  • 为什么不将用户 ID 存储为固定宽度,方法是用空格或其他东西填充它们。然后你可以计算一个条目的偏移量并直接寻找它。
  • @gordy:真正的问题是:他为什么不创建数据库?如果您的项目太大以至于您有 3e10 个用户 ID,那么将它们存储在文本文件中对我来说看起来很糟糕。

标签: java file random


【解决方案1】:

你可以这样做:

  • 获取文件的大小(以字节为单位)
  • 选择一个字节([0..file.length()] 中随机选择的数字 - RandomAccessFile
  • 寻找文件中的那个位置 (file.seek(number))
  • 寻找下一个\n字符(file.seek(1))之后的位置
  • 读线 (file.readLine())

例如...

这样你就不必存储任何东西了。

理论上的示例 sn-p 可能如下所示(包含一些副作用):

File f = new File("D:/abc.txt");
RandomAccessFile file;
try {
    file = new RandomAccessFile(f, "r");
    long file_size = file.length();
    long chosen_byte = (long)(Math.random() * file_size);

    file.seek(chosen_byte);

    for (;;)
    {
        byte a_byte = file.readByte();
        char wordChar = (char)a_byte;
        if (chosen_byte >= file_size || wordChar == '\n' || wordChar == '\r' || wordChar == -1) break;
        else chosen_byte += 1;
        System.out.println("\"" + Character.toString(wordChar)  + "\"");
    }

    int chosen = -1;
    if (chosen_byte < file_size) 
    {
        String s = file.readLine();
        chosen = Integer.parseInt(s);
        System.out.println("Chosen id : \"" + s  + "\"");
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}


编辑: 完整的工作(理论上)类

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;


public class Main {

    /**
     * WARNING : This piece of code requires that the input file terminates by a BLANK line !
     * 
     * @param args
     * @throws Exception 
     */
    public static void main(String[] args) throws Exception {

        File f = new File("D:/abc.txt");
        RandomAccessFile file;

        try {

            file = new RandomAccessFile(f, "r");
            long file_size = file.length();

            // Let's start
            long chosen_byte = (long)(Math.random() * (file_size - 1));
            long cur_byte = chosen_byte;

            // Goto starting position
            file.seek(cur_byte);

            String s_LR = "";
            char a_char;

            // Get left hand chars
            for (;;)
            {
                a_char = (char)file.readByte();
                if (cur_byte < 0 || a_char == '\n' || a_char == '\r' || a_char == -1) break;
                else 
                {
                    s_LR = a_char + s_LR;
                    --cur_byte;
                    if (cur_byte >= 0) file.seek(cur_byte);
                    else break;
                }
            }

            // Get right hand chars
            cur_byte = chosen_byte + 1;
            file.seek(cur_byte);
            for (;;)
            {
                a_char = (char)file.readByte();
                if (cur_byte >= file_size || a_char == '\n' || a_char == '\r' || a_char == -1) break;
                else 
                {
                    s_LR += a_char;
                    ++cur_byte;
                }
            }

            // Parse ID
            if (cur_byte < file_size) 
            {
                int chosen_id = Integer.parseInt(s_LR);
                System.out.println("Chosen id : " + chosen_id);
            }
            else
            {
                throw new Exception("Ran out of bounds. But this usually never happen...");
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}


希望这作为实现不会太错误(那些日子我更喜欢 C++)...

【讨论】:

  • +1。为此,您需要使用 RandomAccessFile。通过谷歌搜索查看它。
  • 这是一个聪明的解决方案。您也可以从文件中的某个随机位置读取一个 30 字节的块并在两个 \n 之间进行解析
  • 有一个问题是更长的行更容易被选中
  • @PeterLawrey 好想法:算法不会很统一。理想情况下,我们会在文件中使用相同大小的 id(最短的以零为前缀:0002565642)并使用例如Mersenne Twister RNG
  • Golgauth,默认的 Java Random 类足以完成此任务(并且还会产生均匀分布)。无需将 MT-19937 带入其中。我们在这里讨论的不是蒙特卡洛模拟或多维值。
【解决方案2】:

您可以解析文件并将偏移量存储在 int[] 数组中,而不是将用户 ID 存储在散列中 - 30M 将占用约 120MB 的 RAM。

或者,如果您可以通过某种方式更改或预处理文件,您可以通过填充用户 ID 或使用二进制格式将格式更改为固定宽度。

【讨论】:

  • 或者只是解析文件并将用户 ID 存储在 int 数组中。
  • @Joey doh!是的,现在看起来很明显
【解决方案3】:

OP 声明:“我知道那个大文本文件中用户 ID 的总数”。叫这个N。

  • 生成一个介于 1 和 N 之间的随机数。
  • 读取行 (BufferedReader) 直到到达第 N 行。
  • 完成

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-10
    • 1970-01-01
    • 1970-01-01
    • 2014-07-29
    • 2012-08-15
    • 1970-01-01
    相关资源
    最近更新 更多