Идентификация заголовка с помощью Regex - PullRequest
0 голосов
/ 08 июня 2018

Мне интересно, как я могу идентифицировать заголовки с разными стилями числовой маркировки с помощью одного или нескольких регулярных выражений, предполагая, что иногда стили перекрываются между документами.Цель состоит в том, чтобы извлечь все подзаголовки и данные для определенного заголовка в каждом файле, но эти файлы не стандартизированы.Регулярные выражения - даже правильный подход?

Я работаю над программой, которая анализирует файл .pdf и ищет определенный раздел.Как только он находит раздел, он находит все подразделы этого раздела и их содержание и сохраняет его в dictionary<string, string>.Я начинаю с чтения всего pdf в строку, а затем использую эту функцию, чтобы найти раздел «маркировка».

private string GetMarkingSection(string text)
    {
      int startIndex = 0;
      int endIndex = 0;
      bool startIndexFound = false;
      Regex rx = new Regex(HEADINGREGEX);
      foreach (Match match in rx.Matches(text))
      {
        if (startIndexFound)
        {
          endIndex = match.Index;
          break;
        }
        if (match.ToString().ToLower().Contains("marking"))
        {
          startIndex = match.Index;
          startIndexFound = true;
        }
      }
      return text.Substring(startIndex, (endIndex - startIndex));
    }

После того, как раздел маркировки найден, я использую его для поиска подразделов.

private Dictionary<string, string> GetSubsections(string text)
    {
      Dictionary<string, string> subsections = new Dictionary<string, string>();
      string[] unprocessedSubSecs = Regex.Split(text, SUBSECTIONREGEX);
      string title = "";
      string content = "";
      foreach(string s in unprocessedSubSecs)
      {
        if(s != "") //sometimes it pulls in empty strings
        {
          Match m = Regex.Match(s, SUBSECTIONREGEX);
          if (m.Success)
          {
            title = s;
          }
          else
          {
            content = s;
            if (!String.IsNullOrWhiteSpace(content) && !String.IsNullOrWhiteSpace(title))
            {
              subsections.Add(title, content);
            }
          }
        }
      }
      return subsections;
    }

Получение этих методов для работы так, как я хочу, не является проблемой, проблема в том, чтобы заставить их работать с каждым из документов. Я работаю над коммерческим приложением, поэтому любой API, для которого требуется лицензия, не будет работать для меня. Этим документам где-то от 1 до 16 лет, поэтому форматирование довольно сильно различается. Вот ссылка на некоторые примеры заголовков и подзаголовков из различных документов. Но, чтобы упростить задачу, я использую шаблоны регулярных выражений:

  • Заголовок: (?m)^(\d+\.\d+\s[ \w,\-]+)\r?$
  • Подзаголовок: (?m)^(\d\.[\d.]+ ?[ \w]+) ?\r?$
  • Мастер-ключ: (?m)^(\d\.?[\d.]*? ?[ \-,:\w]+) ?\r?$

Поскольку некоторые заголовки используют формат подзаголовка в других документах, я не могу использовать этот заголовокрегулярное выражение для каждого файла, и то же самое относится к моему регулярному выражению подзаголовка.

Моя альтернатива этому заключалась в том, что я собирался написать мастер-ключ (указанный в ссылке на регулярное выражение), чтобы идентифицировать все типы заголовков, а затем найтипоследний экземпляр числового символа в каждом заголовке (5.1.X), а затем найдите 5.1.X + 1, чтобы найти конец этого раздела.

Вот тогда я столкнулся с другой проблемой.Некоторые из этих файлов не имеют абсолютно правильной структуры.Большинство из них выходят из 5.2-> 7.1.5 (ожидается 5.2-> 5.3 / 6.0)

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

Вот мой обновленный метод GetMarkingSection:

private Dictionary<string, string> GetMarkingSection(string text)
    {
      var headingRegex = HEADING1REGEX;
      var subheadingRegex = HEADING2REGEX;
      Dictionary<string, string> markingSection = new Dictionary<string, string>();

      if (Regex.Matches(text, HEADING1REGEX, RegexOptions.Multiline | RegexOptions.Singleline).Count > 0)
      {
        foreach (Match m in Regex.Matches(text, headingRegex, RegexOptions.Multiline | RegexOptions.Singleline))
        {
          if (Regex.IsMatch(m.ToString(), HEADINGMASTERKEY))
          {
            if (m.Groups[2].Value.ToLower().Contains("marking"))
            {
              var subheadings = Regex.Matches(m.ToString(), subheadingRegex, RegexOptions.Multiline | RegexOptions.Singleline);
              foreach (Match s in subheadings)
              {
                markingSection.Add(s.Groups[1].Value + " " + s.Groups[2].Value, s.Groups[3].Value);
              }
              return markingSection;
            }
          }
        }
      }
      else
      {
        headingRegex = HEADING2REGEX;
        subheadingRegex = HEADING3REGEX;

        foreach(Match m in Regex.Matches(text, headingRegex, RegexOptions.Multiline | RegexOptions.Singleline))
        {
          if(Regex.IsMatch(m.ToString(), HEADINGMASTERKEY))
          {
            if (m.Groups[2].Value.ToLower().Contains("marking"))
            {
              var subheadings = Regex.Matches(m.ToString(), subheadingRegex, RegexOptions.Multiline | RegexOptions.Singleline);
              foreach (Match s in subheadings)
              {
                markingSection.Add(s.Groups[1].Value + " " + s.Groups[2].Value, s.Groups[3].Value);
              }
              return markingSection;
            }
          }
        }
      }
      return null;
    }

Вот несколько примеров файлов PDF: Style 1 Style 2

1 Ответ

0 голосов
/ 19 июня 2018

Проверьте, работает ли этот подход:

var heading1Regex = @"^(\d+)\s(?<title>.*?)$\n(?<content>.*?)$\n*(?=^\d+\s|\Z)";

Демо

var heading2Regex = @"^(\d+)\.(\d+)\s(?<title>.*?)$\n(?<content>.*?)$\n*(?=^\d+\.\d+\s|\Z)";

Демо

var heading3Regex = @"^(\d+)\.(\d+)\.(\d+)\s(?<title>.*?)$\n(?<content>.*?)$\n*(?=^\d+\.\d+\.\d+\s|\Z)";

Демо

Для каждого файла PDF:

var headingRegex = heading1Regex;
var subHeadingRegex = heading2Regex;

if there are any matches for headingRegex
{
    for each match, find matches for subHeadingRegex
}
else
{
    var headingRegex = heading2Regex;
    var subHeadingRegex = heading3Regex;
    //repeat same steps
}

1.Крайний случай 1: после 5.2 наступает 7.1.3

Как показано здесь , получить соответствие основного раздела, используя heading2Regex.

преобразовать group1 совпадения в целое число

int.TryParse(match.group1, out var headingIndex);

получить совпадения подразделов для heading3Regex

для каждого совпадения подраздела, преобразовать group1 в целое число.

int.TryParse(match.group1, out var subHeadingIndex);

проверить, равен ли headingIndex subHeadingIndex.если не справиться соответственно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...