Сопоставление потокового паттерна с использованием 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 соответственно. Есть две проблемы с этим кодом:
- Это очень медленно, чтобы найти совпадение.
- Только совпадает с первой страницей.
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"
В приведенном выше коде все еще есть возможности для улучшения. Но это должно дать вам представление о том, как начать.