获取出现某个单词/文本的行号

时间:2015-04-30 13:14:59

标签: java stream

我有什么:我有一个逐行读取的文件。这些行不计入文件中。

我想做什么:我想计算一个流中的每一行,只返回发生某个文本的数字。

到目前为止我有什么:

public static Integer findLineNums(String word)
        throws IOException {

    final Map<String, Integer> map = new HashMap<>();
    final List<String> lines = Files.lines(Paths.get(PATH)).collect(Collectors.toList());                 
    IntStream.rangeClosed(0, lines.size()-1).forEach(f -> map.put(lines.get(f), f+1));

    return map.get(word);
}

问题:如何仅使用SINGLE流来执行此操作?

编辑问题:我想在Stream中做所有事情,这也包括累积到列表中。

最佳案例场景如下:

Files.lines(Paths.get(PATH)).superAwesomeStreamFuncs().collect(Collectors.toList());
编辑:在我的情况下,我只返回一个整数,但我希望得到类似整数列表的东西。

3 个答案:

答案 0 :(得分:2)

以下代码段会创建一个List<Integer>,其中的行包含单词

String word = "foo";
List<Integer> matchedLines = new ArrayList<>();
final List<String> lines = Files.readAllLines(Paths.get("word_list.txt"));
IntStream.rangeClosed(0, lines.size() - 1).forEach(f -> {
    if (lines.get(f).contains(word)) {
        matchedLines.add(++f);
    }
});
System.out.println("matchedLines = " + matchedLines);

假设文件word_list.txt

foo
bar
baz
foobar
barfoo

输出

matchedLines = [1, 4, 5]

修改要使用单个流解决此问题,请创建自定义Consumer

public class MatchingLines {

    static class MatchConsumer implements Consumer<String> {
        private int count = 0;
        private final List<Integer> matchedLines = new ArrayList<>();
        private final String word;

        MatchConsumer(String word) {
            this.word = word;
        }

        @Override
        public void accept(String line) {
            count++;
            if (line.contains(this.word)) {
                matchedLines.add(count);
            }
        }

        public List<Integer> getResult() {
            return matchedLines;
        }
    }

    public static void main(String[] args) throws IOException {
        MatchConsumer matchConsumer = new MatchConsumer("foo");
        Files.lines(Paths.get("word_list.txt")).forEach(matchConsumer);
        System.out.println("matchedLines = " + matchConsumer.getResult());
    }
}

答案 1 :(得分:2)

这有效:

int[] i = new int[]{0}; // trick to make it final
List<Integer> hits = <your stream>
  .map(s -> s.contains(word) ? ++i[0] : - ++i[0])
  .filter(n -> n > 0)
  .collect(Collectors.toList());

这里的主要“技巧”是使用数组,其引用不会改变(即它是“有效的最终”,但它允许我们改变它的(仅)元素作为计数器,无论如何都以内联方式递增。快速过滤器会抛出不匹配。

一些测试代码:

String word = "foo";
int[] i = new int[]{0};
List<Integer> hits = Stream.of("foo", "bar", "foobar")
.map(s -> s.contains(word) ? ++i[0] : - ++i[0])
.filter(n -> n > 0)
.collect(Collectors.toList());
System.out.println(hits);

输出:

[1, 3]

答案 2 :(得分:0)

此方法返回由文件中的数字映射的行。

public static Map<String, Integer> findLineNums(Path path, String word) throws IOException {

        final Map<String, Integer> map = new HashMap<>();
        int lineNumber = 0;
        Pattern pattern = Pattern.compile("\\b" + word + "\\b");

        try (BufferedReader reader = Files.newBufferedReader(path)) {
            String line = null;
            while ((line = reader.readLine()) != null) {
                lineNumber++;
                if (pattern.matcher(line).find()) {
                    map.put(line, lineNumber);
                }
            }
        }
        for (String line : map.keySet()) {
            Integer lineIndex = map.get(line);
            System.out.printf("%d  %s\n", lineIndex, line);
        }
        return map;
    }

BufferedReaderFiles.lines流一样逐行读取文件。