Сопоставление потокового паттерна с использованием Regex

Я хотел бы проанализировать большой текстовый файл, отформатированный в Warc версии 0.9. Образец такого текста здесь. Если вы посмотрите на него, вы увидите, что весь документ состоит из списка следующих записей.

[Warc Headers]

[HTTP Headers]

[HTML Content]

Мне нужно извлечь URL-адрес и HTML-содержимое из каждой записи (обратите внимание, что образец файла состоит из нескольких записей страницы, каждая из которых отформатирована, как содержимое выше).

Я использовал следующее регулярное выражение в Java:

Pattern.compile("warc/0\\.9\\s\\d+\\sresponse\\s(\\S+)\\s.*\n\n.*\n\n(.*)\n\n", Pattern.DOTALL)

Где группа 1 и 2 представляют URL и содержимое HTML соответственно. Есть две проблемы с этим кодом:

  1. Это очень медленно, чтобы найти совпадение.
  2. Только совпадает с первой страницей.

Java-коды:

if(mStreamScanner.findWithinHorizon(PAGE_ENTRY, 0) == null){
    return null;
} else {
    MatchResult result = mStreamScanner.match();
    return new WarcPageEntry(result.group(1), result.group(2));
}

Вопросы:

  • Почему мой код анализирует только первую запись страницы?
  • Есть ли более быстрый способ для анализа большого текста в потоковом режиме?

1 ответ

Я бы не стал решать эти огромные строки HTML с помощью регулярных выражений. Как насчет того, чтобы полагаться на структуру документа?

Например, вот так:

HashMap<String, String> output = new HashMap<>();
Pattern pattern = Pattern.compile("^warc\\/0\\.9\\s\\d+\\sresponse\\s(\\S+)\\s.*");

try (InputStreamReader is = new InputStreamReader(new FileInputStream("excerpt.txt"), "UTF-8")) {               
    try (BufferedReader br = new BufferedReader(is)) {      
        String line;        
        while ((line = br.readLine()) != null) {
            Matcher matcher = pattern.matcher(line);

            if (matcher.matches()) {
                entityLoop: while (true) {
                    String url = matcher.group(1);

                    // skip header
                    int countEmptyLines = 0;
                    while ((line = br.readLine()) != null) {
                        if ("".equals(line)) {
                            countEmptyLines++;
                            if (countEmptyLines == 2) break;
                        }
                    }

                    // extract HTML
                    StringBuilder sb = new StringBuilder();
                    while ((line = br.readLine()) != null) {
                        matcher = pattern.matcher(line);
                        if (matcher.matches()) { 
                            // got all HTML; store our findings
                            output.put(url, sb.toString());
                            continue entityLoop; 
                        }
                        sb.append(line);
                    }
                    break; // no more url/html-entities available
                }
            }
        }
    }       
} catch (IOException e) {
    // do something smart
}

// now all your extracted data is stored in "output"

В приведенном выше коде все еще есть возможности для улучшения. Но это должно дать вам представление о том, как начать.

Другие вопросы по тегам