使用Java 8在文本文件中查找字符串

3

我有一个很长的文本文件,我想读取并从中提取一些数据。使用JavaFX和FXML,我正在使用FileChooser加载文件以获取文件路径。 我的controller.java具有以下内容:

private void handleButtonAction(ActionEvent event) throws IOException {
        FileChooser fileChooser = new FileChooser();
        FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("TXT files (*.txt)", "*.txt");
        fileChooser.getExtensionFilters().add(extFilter);
        File file = fileChooser.showOpenDialog(stage);
        System.out.println(file);
         stage = (Stage) button.getScene().getWindow();


    }

文本文件示例:请注意,一些文件内容分为两行。例如,-Ba\ 10.10.10.3 是第一行的一部分。

net ip-interface create 10.10.10.2 255.255.255.128 MGT-1 -Ba \
10.10.10.3
net ip-interface create 192.168.1.1 255.255.255.0 G-1 -Ba \
192.168.1.2 
net route table create 10.10.10.5 255.255.255.255 10.10.10.1 -i \
MGT-1
net route table create 10.10.10.6  255.255.255.255 10.10.10.1 -i \
MGT-1

我正在寻找一种方法来搜索这个(文件)并输出以下内容:
MGT-1 ip-interface 10.10.10.2 
MGT-1 Backup ip-interface 10.10.10.3
G-1 ip-interface 192.168.1.1
G-1 Backup Ip-interface 192.168.1.2
MGT-1 route 10.10.10.5 DFG 10.10.10.1
MGT-1 route 10.10.10.6 DFG 10.10.10.1

你会使用 awk 吗?http://jawk.sourceforge.net/ 你的结果中 DFG 是从哪里来的? - kukis
谢谢您的评论!我是Java的新手,所以正在寻找指引。如果需要使用AWK,那就用吧。DFG代表默认网关。网络路由表(通过默认网关10.10.10.1为10.10.10.5-6创建静态路由)。 - Moe
如果内存不受限制,您可以使用以下代码:List<String> lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8); 以获取包含所有行的列表。然后,您可以循环遍历这些行并按照您的意愿进行解析。 - assylias
有没有关于如何解析它们以获得上面的输出的建议?例如,对于模式“MGT-1 -Ba \”,我如何输出下一行?10.10.10.3。我该怎么做呢?或者我如何在“ip-interface create”之后输出IP地址。10.10.10.2?我正在寻找有关如何执行解析以获取上述输出数据的想法。我听说Java 8使用流,这是我可以在这里使用的东西吗? - Moe
“备份”(不是输入中的)和“IP接口”(大小写变化)来自哪里? - Bohemian
1个回答

4
当然,您可以使用BufferedReader.linesFiles.lines将输入文件作为行流读取。但是在这里棘手的问题是如何处理结尾处的"\"。有几种可能的解决方案。您可以编写自己的Reader,它包装了现有的Reader并忽略了跟随EOL的斜杠。或者,您可以编写一个自定义的IteratorSpliterator,它以BufferedReader.lines流作为输入,并处理此情况。我建议使用我的StreamEx库,它已经有一个名为collapse的方法来完成这样的任务:
StreamEx.ofLines(reader).collapse((a, b) -> a.endsWith("\\"), 
                                  (a, b) -> a.substring(0, a.length()-1).concat(b));

第一个参数是谓词,应用于相邻的两行,如果这些行应该合并,则返回true。第二个参数是实际合并两行的函数(我们通过substring来切割斜杠,然后连接下一行)。
现在,您可以通过空格拆分该行,并根据您的任务将其转换为一个或两个输出行。最好通过单独的方法来完成。整个代码如下:
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.regex.Pattern;
import java.util.stream.Stream;

import javax.util.streamex.StreamEx;

public class ParseFile {
    static Stream<String> convertLine(String[] fields) {
        switch(fields[1]) {
        case "ip-interface":
            return Stream.of(fields[5]+" "+fields[1]+" "+fields[3],
                             fields[5]+" Backup "+fields[1]+" "+fields[7]);
        case "route":
            return Stream.of(fields[8]+" route "+fields[4]+" DFG "+fields[6]);
        default:
            throw new IllegalArgumentException("Unrecognized input: "+
                                               String.join(" ", fields));
        }
    }

    static Stream<String> convert(Reader reader) {
        return StreamEx.ofLines(reader)
                .collapse((a, b) -> a.endsWith("\\"), 
                          (a, b) -> a.substring(0, a.length()-1).concat(b))
                .map(Pattern.compile("\\s+")::split)
                .flatMap(ParseFile::convertLine);
    }

    public static void main(String[] args) throws IOException {
        try(Reader r = new InputStreamReader(
            ParseFile.class.getResourceAsStream("test.txt"))) {
            convert(r).forEach(System.out::println);
        }
    }
}

谢谢,看起来这个方法可以解决问题,正在努力实现中...但是有两个问题:1)我该如何将我的文件路径传递给你的ParseFile.java,并从我的controller.java(上面提到的)返回输出?2)我该如何在不使用你的包的情况下(为了学习过程),将两行代码合并并打印所需内容。我似乎找不到这方面的信息。 - Moe
我从ParseFile中删除了你的main方法,并在我的控制器中添加了try(Reader r = new InputStreamReader(ParseFile.class.getResourceAsStream("file"))) { convert(r).forEach(System.out::println); },但它没有起作用。 - Moe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接