将文件合并到一个新的大文件中,直到用户ID的数量变为1000万

时间:2013-06-19 20:43:36

标签: java file linkedhashset

我在一个文件夹中有大约100个文件。每个文件都有这样的数据,每行都类似于用户ID。

960904056
6624084
1096552020
750160020
1776024
211592064
1044872088
166720020
1098616092
551384052
113184096
136704072

我正在尝试继续将该文件夹中的文件合并到一个新的大文件中,直到该新文件中的用户ID总数达到1000万。

我能够读取特定文件夹中的所有文件,然后继续在linkedhashset中添加这些文件中的用户ID。然后我想要查看hashset的大小是否为1000万,如果它是1000万,那么将所有这些用户ID写入新的文本文件。这是可行的解决方案吗?

这1000万个数字应该是可配置的。在未来,如果我需要改变1000万1o 50Million 那我就能做到。

以下是我到目前为止的代码

public static void main(String args[]) {

    File folder = new File("C:\\userids-20130501");
    File[] listOfFiles = folder.listFiles();

    Set<String> userIdSet = new LinkedHashSet<String>();
    for (int i = 0; i < listOfFiles.length; i++) {
        File file = listOfFiles[i];
        if (file.isFile() && file.getName().endsWith(".txt")) {
            try {
                List<String> content = FileUtils.readLines(file, Charset.forName("UTF-8"));
                userIdSet.addAll(content);
                if(userIdSet.size() >= 10Million) {
                    break;
                }
                System.out.println(userIdSet);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

对此有何帮助?还有更好的方法来做同样的过程吗?

3 个答案:

答案 0 :(得分:1)

继续我们离开的地方。 ;)

您可以使用FileUtils将文件与writeLines()方法一起编写。

试试这个 -

public static void main(String args[]) {

File folder = new File("C:\\userids-20130501");

Set<String> userIdSet = new LinkedHashSet<String>();
int count = 1;
for (File file : folder.listFiles()) {
    if (file.isFile() && file.getName().endsWith(".txt")) {
        try {
            List<String> content = FileUtils.readLines(file, Charset.forName("UTF-8"));
            userIdSet.addAll(content);
            if(userIdSet.size() >= 10Million) {
                File bigFile = new File("<path>" + count + ".txt");
                FileUtils.writeLines(bigFile, userIdSet);
                count++;
                userIdSet = new LinkedHashSet<String>(); 
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
  }
}

如果在LinkedHashSet中保存数据的目的只是将其再次写入另一个文件,那么我有另一个解决方案。

编辑以避免OutOfMemory异常

public static void main(String args[]) {
File folder = new File("C:\\userids-20130501");

int fileNameCount = 1;
int contentCounter = 1;
File bigFile = new File("<path>" + fileNameCount + ".txt");
boolean isFileRequired = true;
for (File file : folder.listFiles()) {
    if (file.isFile() && file.getName().endsWith(".txt")) {
        try {
            List<String> content = FileUtils.readLines(file, Charset.forName("UTF-8"));
            contentCounter += content.size();
            if(contentCounter < 10Million) {
                FileUtils.writeLines(bigFile, content, true);
            } else {
                fileNameCount++;
                bigFile = new File("<path>" + fileNameCount + ".txt");
                FileUtils.writeLines(bigFile, content);
                contentCounter = 1;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
  }
}

答案 1 :(得分:0)

如果您在从文件中读取的同时进行写入,则可以避免将Set用作中间存储。你可以这样做,

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;


public class AppMain {
  private static final int NUMBER_REGISTERS = 10000000;

  private static String[] filePaths = {"filePath1", "filePaht2", "filePathN"}; 
  private static String mergedFile = "mergedFile";


  public static void main(String[] args) throws IOException {
    mergeFiles(filePaths, mergedFile);
  }

  private static void mergeFiles(String[] filePaths, String mergedFile) throws IOException{
    BufferedReader[] readerArray = createReaderArray(filePaths);
    boolean[] closedReaderFlag = new boolean[readerArray.length];

    PrintWriter writer = createWriter(mergedFile);

    int currentReaderIndex = 0;
    int numberLinesInMergedFile = 0;

    BufferedReader currentReader = null;
    String currentLine = null;
    while(numberLinesInMergedFile < NUMBER_REGISTERS && getNumberReaderClosed(closedReaderFlag) < readerArray.length){
      currentReaderIndex = (currentReaderIndex + 1) % readerArray.length; 

      if(closedReaderFlag[currentReaderIndex]){
       continue;
      }

      currentReader = readerArray[currentReaderIndex];

      currentLine = currentReader.readLine();
      if(currentLine == null){
       currentReader.close();
       closedReaderFlag[currentReaderIndex] = true;
       continue;
      }

      writer.println(currentLine);
      numberLinesInMergedFile++;
    }

    writer.close();
    for(int index = 0; index < readerArray.length; index++){
      if(!closedReaderFlag[index]){
       readerArray[index].close();
      }
    }

  }

  private static BufferedReader[] createReaderArray(String[] filePaths) throws FileNotFoundException{
    BufferedReader[] readerArray = new BufferedReader[filePaths.length];

    for (int index = 0; index < readerArray.length; index++) {
      readerArray[index] = createReader(filePaths[index]);
    }

    return readerArray;
  }

  private static BufferedReader createReader(String path) throws FileNotFoundException{
    BufferedReader reader = new BufferedReader(new FileReader(path));

    return reader;
  }

  private static PrintWriter createWriter(String path) throws FileNotFoundException{
    PrintWriter writer = new PrintWriter(path);

    return writer;
  }

  private static int getNumberReaderClosed(boolean[] closedReaderFlag){
    int count = 0;

    for (boolean currentFlag : closedReaderFlag) {
      if(currentFlag){
    count++;
      }
    }

    return count;
  }
}

答案 2 :(得分:0)

你的方式,你可能会耗尽内存,你在 userIdSet 中保留了不必要的记录。

可以改进代码的细微修改如下:

public static void main(String args[]) {

    File folder = new File("C:\\userids-20130501");
    File[] listOfFiles = folder.listFiles();

    // there's no need for the userIdSet!
    //Set<String> userIdSet = new LinkedHashSet<String>();

    // Instead I'd go for a counter ;)
    long userIdCount = 0;

    for (int i = 0; i < listOfFiles.length; i++) {
        File file = listOfFiles[i];
        if (file.isFile() && file.getName().endsWith(".txt")) {
            try {
                List<String> content = FileUtils.readLines(file, Charset.forName("UTF-8"));
                // I just want to know how many lines there are...
                userIdCount += content.size();

                // my guess is you'd probably want to print what you've got
                // before a possible break?? - You know better!
                System.out.println(content);

                if(userIdCount >= 10Million) {
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

就像我指出的那样,只需稍加修改即可。我不打算对您的代码进行非常详细的分析。我只是指出了一个明显错误的设计。

最后,在您声明 System.out.println(content); 的地方,您可能会考虑在此时写入文件。

如果您一次写入一行文件,则try-catch块可能如下所示:

try {
    List<String> content = FileUtils.readLines(file, Charset.forName("UTF-8"));

    for(int lineNumber = 0; lineNumber < content.size(); lineNumber++){
        if(++userIdCount >= 10Million){
           break;
        }
        // here, write to file... But I will use simple System.out.print for example
        System.out.println(content.get(lineNumber));
    }
} catch (IOException e) {
    e.printStackTrace();
}

您的代码可以通过多种方式进行改进,但我没有时间这样做。但我希望我的建议能够让你在正确的轨道上走得更远。干杯!