Как извлечь текст в формате из PDF или XPS, используя C#? - PullRequest
4 голосов
/ 14 июля 2020

Как извлечь текст в формате из PDF или XPS с помощью C#?

У меня есть файлы PDF / XPS, созданные другим программным обеспечением для создания отчетов. Файл в основном включает таблицы, в которых перечислены некоторые данные.

iText может извлекать текст из файлов PDF, но затем теряет какой-то формат, например, для таблицы ниже извлеченный текст:

введите описание изображения здесь

Faults
Count FMI Lookup Code Description Component Status
Active Body Controller Heating Ventilation/Air Conditioning (HVAC) Control 
Head Air Inlet DM1.  HVAC motor in wrong position or 
jammed
SPN 3984 2 126
Active Engine SAE - Catalyst 1 System Monitor - Root cause not known SID 380 11 N/A
Inactive Engine SAE - Crankcase Pressure - Data valid but above normal 
operational range - Most severe level
PID 101 0 N/A
Inactive Engine SAE - Crankcase Pressure - Data erratic, intermittent or 
incorrect
PID 101 2 N/A

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

Я также пытался преобразовать PDF в html, но потом обнаружил, что html не включает фактический текст, который использует SVG в html. Так что я не смог получить фактический текст.

Есть ли способ сделать это с помощью C#? Какие-либо предложения? Любая библиотека, лучше бесплатные?

Спасибо

Ответы [ 2 ]

1 голос
/ 16 июля 2020

Вы можете извлечь форматированный текст, используя Docoti c .Pdf (отказ от ответственности: я соавтор). Вот пример кода basi c:

using (var pdf = new PdfDocument("your_document.pdf"))
{
    string formattedText = pdf.GetTextWithFormatting();
    using (var writer = new StreamWriter("formatted.txt"))
        writer.Write(formattedText);
}

Результат примера: enter image description here

After that you can detect columns by whitespaces. For example, treat a sequence of 3+ whitespaces as a column separator.

You can find other text extraction techniques in эта статья . Например, эти методы тоже могут быть полезны:

  • извлечение текста из определенной области c (полезно, если страница содержит разные таблицы или смесь обычного текста с таблицами)
  • извлекать подробную информацию о каждом фрагменте текста (полезно, если вы хотите создать собственный журнал обнаружения таблиц c)
  • извлекать текст с векторными путями (полезно, если вы хотите соблюдать границы таблицы в своем алгоритме определения настраиваемой таблицы)
1 голос
/ 14 июля 2020

Если вы знаете все возможные значения для префиксов «Статус», «Компонент» и «Код поиска», вы можете использовать такой подход: вы можете видеть, что каждая запись структурирована как «Статус-Компонент-Описание-Код поиска- FMI- Считай ». Добавьте объект:

class Fault
{
    public string Count { get; set; }
    public string FMI { get; set; }
    public string LookupCode { get; set; }
    public string Description { get; set; }
    public string Component { get; set; }
    public string Status { get; set; }

    public override string ToString() =>
        $"Status: {Status}; Component: {Component}; Description: {Description}; LookupCode: {LookupCode}; FMI: {FMI}; Count: {Count}";
}

И сопоставьте ввод текста следующим образом:

class Parser
{
    private static readonly IReadOnlyList<string> statuses = new[]
    {
        "Active",
        "Inactive"
        // etc
    };

    private static readonly IReadOnlyList<string> components = new[]
    {
        "Body Controller",
        "Engine"
        // etc
    };

    private static readonly IReadOnlyList<string> lookupPrefixes = new[]
    {
        "SPN",
        "SID",
        "PID"
        // etc
    };

    public static IEnumerable<Fault> Parse(string str)
    {
        var lines = str.Split(Environment.NewLine).Skip(2);
        foreach(var group in GetGroups(lines))
        {
            var words = group.SelectMany(line => line.Split()).ToList();

            var i = 1;
            string status = default;
            while (!statuses.Contains(status = string.Join(' ', words.Take(i)))) i++;
            words = words.Skip(i).ToList();

            i = 1;
            string component = default;
            while (!components.Contains(component = string.Join(' ', words.Take(i)))) i++;
            words = words.Skip(1).Reverse().ToList();

            string count = words[0];

            string fmi = words[1];
            words = words.Skip(2).ToList();

            i = words.FindIndex(word => lookupPrefixes.Contains(word)) + 1;
            string code = string.Join(' ', words.Take(i).Reverse());

            string description = string.Join(' ', words.Skip(i).Reverse());

            yield return new Fault
            {
                Status = status,
                Component = component,
                Description = description,
                LookupCode = code,
                FMI = fmi,
                Count = count
            };
        }
    }

    private static IEnumerable<IEnumerable<string>> GetGroups(IEnumerable<string> lines)
    {
        var list = new List<string> { lines.First() };

        foreach (var line in lines.Skip(1))
        {
            if(statuses.Any(status => line.StartsWith(status)))
            {
                yield return list;

                list = new List<string>();
            }
            list.Add(line);
        }

        yield return list;
    }
}

Затем вы можете использовать его:

class Program
{
    private static readonly string input =
        @"Faults
Count FMI Lookup Code Description Component Status
Active Body Controller Heating Ventilation/Air Conditioning(HVAC) Control
Head Air Inlet DM1.HVAC motor in wrong position or
jammed
SPN 3984 2 126
Active Engine SAE - Catalyst 1 System Monitor - Root cause not known SID 380 11 N/A
Inactive Engine SAE - Crankcase Pressure - Data valid but above normal
operational range - Most severe level
PID 101 0 N/A
Inactive Engine SAE - Crankcase Pressure - Data erratic, intermittent or
incorrect
PID 101 2 N/A";

    static void Main()
    {
        new Program().Run();
    }

    private void Run()
    {
        foreach (var result in Parser.Parse(input))
            Console.WriteLine(result);
    }
}

и получить:

Status: Active; Component: Body Controller; Description: Controller Heating Ventilation/Air Conditioning(HVAC) Control Head Air Inlet DM1.HVAC motor in wrong position or jammed; LookupCode: SPN 3984; FMI: 2; Count: 126
Status: Active; Component: Engine; Description: SAE - Catalyst 1 System Monitor - Root cause not known; LookupCode: SID 380; FMI: 11; Count: N/A
Status: Inactive; Component: Engine; Description: SAE - Crankcase Pressure - Data valid but above normal operational range - Most severe level; LookupCode: PID 101; FMI: 0; Count: N/A
Status: Inactive; Component: Engine; Description: SAE - Crankcase Pressure - Data erratic, intermittent or incorrect; LookupCode: PID 101; FMI: 2; Count: N/A

Возможна оптимизация решения.

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