用百万字解析一个文件

时间:2016-06-29 11:24:21

标签: java

我已经实现了一些代码来查找txt sample.txt文件中的anagrams字,并在控制台上输出它们。 txt文档在每行中都包含String(word)。

如果我想在txt.file中找到带有百万或二十亿字的字谜词,那么这是正确的使用方法吗?如果不是在这种情况下我应该使用哪种技术?

我感谢任何帮助。

示例

abac
aabc
hddgfs
fjhfhr
abca
rtup
iptu
xyz
oifj
zyx
toeiut
yxz
jrgtoi

oupt

abac aabc abca
xyz zyx yxz

代码

package org.reader;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Test {
    // To store the anagram words
    static List<String> match = new ArrayList<String>();
    // Flag to check whether the checkWorld1InMatch() was invoked.
    static boolean flagCheckWord1InMatch;

    public static void main(String[] args) {
        String fileName = "G:\\test\\sample2.txt";
        StringBuilder sb = new StringBuilder();
        // In case of matching, this flag is used to append the first word to
        // the StringBuilder once.
        boolean flag = true;

        BufferedReader br = null;
        try {
            // convert the data in the sample.txt file to list
            List<String> list = Files.readAllLines(Paths.get(fileName));

            for (int i = 0; i < list.size(); i++) {

                flagCheckWord1InMatch = true;
                String word1 = list.get(i);

                for (int j = i + 1; j < list.size(); j++) {

                    String word2 = list.get(j);

                    boolean isExist = false;

                    if (match != null && !match.isEmpty() && flagCheckWord1InMatch) {
                        isExist = checkWord1InMatch(word1);

                    }

                    if (isExist) {
                        // A word with the same characters was checked before
                        // and there is no need to check it again. Therefore, we
                        // jump to the next word in the list.
                        // flagCheckWord1InMatch = true;
                        break;
                    } else {
                        boolean result = isAnagram(word1, word2);
                        if (result) {

                            if (flag) {
                                sb.append(word1 + " ");
                                flag = false;
                            }

                            sb.append(word2 + " ");

                        }
                        if (j == list.size() - 1 && sb != null && !sb.toString().isEmpty()) {
                            match.add(sb.toString().trim());
                            sb.setLength(0);
                            flag = true;

                        }

                    }

                }
            }

        } catch (

        IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (br != null) {
                    br.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

        for (String item : match) {
            System.out.println(item);
        }

        // System.out.println("Sihwail");

    }

    private static boolean checkWord1InMatch(String word1) {
        flagCheckWord1InMatch = false;
        boolean isAvailable = false;
        for (String item : match) {
            String[] content = item.split(" ");
            for (String word : content) {
                if (word1.equals(word)) {
                    isAvailable = true;
                    break;

                }
            }
        }
        return isAvailable;
    }

    public static boolean isAnagram(String firstWord, String secondWord) {
        char[] word1 = firstWord.toCharArray();
        char[] word2 = secondWord.toCharArray();
        Arrays.sort(word1);
        Arrays.sort(word2);
        return Arrays.equals(word1, word2);
    }

}

5 个答案:

答案 0 :(得分:6)

对于200亿字,你将无法将所有这些字体保存在RAM中,因此您需要一种方法来处理它们。

20,000,000,000字。 Java需要相当多的内存来存储字符串,因此每个字符可以计算2个字节,并且开销至少为38个字节。

这意味着一个字符的20,000,000,000个单词需要800,000,000,000字节或800 GB,这比我所知道的任何计算机都要多。

您的文件将包含少于20,000,000,000个不同的单词,因此如果您只存储一次单词(例如Set),则可以避免内存问题。

答案 1 :(得分:3)

首先是较小的数字。

由于最好使用更强大的数据结构,不要读取核心中的所有行,而是按行读取。

Map<String, Set<String>> mapSortedToWords = new HashMap<>();

Path path = Paths.get(fileName);
try (BufferedReader in = Files.newBufferedReader(Path, StandardCharsets.UTF_8)) {
    for (;;) {
        String word = in.readLine();
        if (word == null) {
            break;
        }
        String key = sorted(word);
        Set<String> words = mapSortedToWords.get(key);
        if (words == null) {
            words = new TreeSet<String>();
            mapSortedToWords.put(key, words);
        }
        words.add(word);
    }
}
for (Set<String> anagrams : mapSortedToWords.values()) {
    if (anagrams.size() > 1) {
        ... anagrams
    }
}

static String sorted(String word) {
    char[] letters = word.toCharArray();
    Arrays.sort(letters);
    return new String(letters);
}

这会在地图中存储一组单词。与abac aabc abca相比。

对于大量存储(sortedLetters,word)的数据库会更好。像Derby或H2这样的嵌入式数据库不会造成安装问题。

答案 2 :(得分:2)

对于您指定的文件大小类型(200亿字),显然您的代码存在两个主要问题,

Shape line = ws.Shapes.AddLine(98, 60, 432, 60);
line.Line.ForeColor.RGB = Color.Red.ToArgb();

AND

List<String> list = Files.readAllLines(Paths.get(fileName)); 

程序中的这两行基本上都是问题,

  1. 你有足够的内存可以一次读取完整的文件吗?
  2. 可以迭代200亿次吗?
  3. 对于大多数系统,上述两个问题的答案都是否定的。

    所以你的目标是减少内存占用量并减少迭代次数。

    因此,您需要按块读取文件块,并使用某种搜索数据结构(如Trie )来存储您的单词。

    对于上述两个主题,您会在SO上找到许多问题,例如

    Fastest way to incrementally read a large file

    Finding anagrams for a given word

    上面的算法说明你必须先为你的单词创建一个字典

    无论如何,我相信你没有现成的答案。拿一个十亿字的文件(这本身就是一项非常困难的任务),看看哪些有效,哪些无效但你的当前代码显然不起作用。

    希望它有所帮助!!

答案 3 :(得分:0)

  

更新

您可以使用地图查找下面的字谜。对于您拥有的每个单词,您可以对其字符进行排序并获取已排序的字符串。所以,这将是你的字谜地图的关键。而这个键的价值将是其他字谜词。

public void findAnagrams(String[] yourWords) {
    Map<String, List<String>> anagrams = new HashMap<String, List<String>>();
    for (String word : yourWords) {
        String sortedWord = sortedString(word);
        List<String> values = anagrams.get(sortedWord);
        if (values == null) 
            values = new LinkedList<>();

        values.add(word);
        anagrams.put(sortedWord, values);
    }

    System.out.println(anagrams);
}

private static String sortedString(String originalWord) {

    char[] chars = originalWord.toCharArray();
    Arrays.sort(chars);
    String sorted = new String(chars);
    return sorted;
}

答案 4 :(得分:0)

使用流来读取文件。这样你只能同时存储一个单词。

FileReader file = new FileReader("file.txt"); //filestream

String word;

while(file.ready()) //return true if there a bytes left in the stream
{
    char c = file.read(); //reads one character
    if(c != '\n') 
    {
        word+=c;
    }
    else {
    process(word); // do whatever you want
    word = "";
    }
}