在流上执行正则表达式

17

我有一些大型文本文件需要进行连续匹配(只是捕获,不替换)。我认为将整个文件保存在内存中并不是一个好主意,而是要使用Reader

根据我的了解,如果有匹配项,它不会跨越超过5行。因此,我的想法是有一种缓冲区,仅保留这5行左右,进行第一次搜索,然后继续。但是它必须“知道”正则表达式匹配结束的地方才能这样做。例如,如果匹配在第2行结束,则应从此处开始下一次搜索。是否可能以一种高效的方式实现这种方法?

6个回答

29

你可以使用 ScannerfindWithinHorizon 方法:

Scanner s = new Scanner(new File("thefile"));
String nextMatch = s.findWithinHorizon(yourPattern, 0);

来自API文档中findWithinHorizon的说明:

如果horizon为0,则忽略horizon并继续搜索输入,寻找指定模式而不受限制。在这种情况下,它可能会缓冲所有输入以寻找该模式。

顺便提一下:当匹配多行时,您可能需要查看常量Pattern.MULTILINEPattern.DOTALL


1
+1;阅读API以了解Scanner如何处理IOException - polygenelubricants

3

Streamflyer 能够在字符流上应用正则表达式。

请注意,我是其作者。


很遗憾,你的库已经不再维护了。 :-( 有什么好的替代品吗? - Benjamin Marwell

2
Java实现的正则表达式引擎似乎不适合流处理。我更倾向于支持另一种基于“导数组合器”的方法。研究员Matt Might在他的博客上发表了有关“导数组合器”的相关文章,并建议在此处使用Scala实现。在我的工作中,我通过添加一些“捕获”功能成功改进了这个实现,但我觉得它可能会对内存消耗产生重大影响。请保留HTML标签。

0
import java.io.*;  //BufferedReader //FileReader //FileWriter //PrintWriter
import java.io.IOException;
import java.util.Scanner;
import java.util.regex.*;

public class ScannerReader { 

    public static void main(String[] args) {

        try {  
            ReadDataFromFileTestRegex("[A-Za-z_0-9-%$!]+@[A-Za-z_0-9-%!$]+\\.[A-Za-z]{2,4}",
                                      "C:\\Users\\Admin\\Desktop\\TextFiles\\Emails.txt",
                                      "C:\\Users\\Admin\\Desktop\\TextFiles\\\\output.txt");
        } catch (Exception e) {
            System.out.println("File is not found");
            e.printStackTrace();
        }       
    }

    public static void ReadDataFromFileTestRegex (String theReg, String FileToRead, String FileToWrite) throws Exception {

        PrintWriter Pout = new PrintWriter(FileToWrite);            
        Pattern p = Pattern.compile(theReg); 
        BufferedReader br = new BufferedReader (new FileReader(FileToRead)); 
        String line = br.readLine();       
        while (line != null) {          
            Matcher m = p.matcher(line);
            while (m.find()) {
                if (m.group().length() != 0) {
                    System.out.println( m.group().trim());
                }             
                System.out.println("Start index: " + m.start());
                System.out.println("End index  : " + m.end());
                Pout.println(m.group());  //print the result to the output file
            }
            line = br.readLine();
        }
        Pout.flush();   
        br.close();
        Pout.close();
    }
}

请问您能否通过添加示例代码用法和示例输出来完善您有前途的帖子? - Stephan

0
也许你正在寻找的是 Scanner.matchAll()。它简化了我的代码。
try(var scanner = new Scanner(Path.of(path), StandardCharsets.UTF_8)){
    var result = scanner.findAll(PATTERN)
                .map(MatchResult::group)
                .collect(Collectors.toSet());
}


-5

使用Java8,您可以相对简单地完成此操作,并且可能是并行的 -

// Create a pattern-matcher
private static final Pattern emailRegex = Pattern.compile("([^,]+?)@([^,]+)");

//Read content of a file
String fileContent = Files.lines(Path.get("/home/testFile.txt")
                              .collect(Collector.join(" "));
// Apply the pattern-matcher
List<String> results = matcherStream(emailRegex.matcher(fileContent))
                           .map(b -> b[2])
                           .collect(Collector.toList()));

另一种方法可以是 -

List<String> results = Files.lines(Path.get("/home/testFile.txt")
                              .parallelStream()
                              .forEach(s -> "use regex")
                              .collect(Collector.toList());

您从另一个答案中获取了此内容,但没有引用它(https://dev59.com/82Af5IYBdhLWcg3wbSMh#24663422),而且甚至没有完全完成。`matcherStream`方法未定义。 - haventchecked

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