💡 Повний робочий приклад доступний на GitHub:
parse-docx-to-markdown-chunks-dotnet

Старий спосіб був болючим

Розбиття Markdown за заголовками — це .NET підготовчий шлях, який перетворює файли Office та PDF у текст розміром з розділ перед вбудовуванням. Без цього команди часто завантажують увесь файл Word в один вектор. Абзац обкладинки та пункт обов’язків ділять одне вбудовування, тому пошук за косинусом ранжує неправильний розділ. Я зіткнувся з цим у трьохсторінковому внутрішньому посібнику: кожен запит спочатку витягав блок заголовка, бо індекс ніколи не бачив структуру # / ##, яку вже написали автори.

Самостійна розробка конвертера DOCX у Markdown означає проходження стилів Open XML, відображення рівнів списків і все ще помилки, коли наступне завантаження — PDF, у якого шлях форматованого Markdown повертає null. Вікна токенів фіксованого розміру ігнорують ці заголовки. За ніч корпуси змішують порожні оболонки з величезними посібниками, бо ніхто спочатку не перевіряє кількість сторінок чи розмір.

Справжня вартість вбудовувань цілого файлу: відповіді з неправильними розділами, витрачений на зайві витяги час на небажані завантаження та відсутність артефактів chunk-01.md, які оператори могли б відкрити під час поганої партії.

Є кращий спосіб

GroupDocs.Parser для .NET замінює цей безлад чотирма цілеспрямованими викликами: GetDocumentInfo, GetFormattedText(FormattedTextMode.Markdown), GetText, коли Markdown дорівнює null, та невеликий обхід заголовків, який записує chunk-NN.md. Той самий тип Parser охоплює розбір Word DOCX та резервний текст PDF, тому ви зберігаєте один шлях сервісу C#.

Вам знадобиться:

  • .NET 8 SDK
  • GroupDocs.Parser 26.10.0 (temporary license)
  • DOCX Word (зразок має 3 сторінки / 14820 байт) та за бажанням PDF
dotnet add package GroupDocs.Parser --version 26.10.0

Новий спосіб: інспектувати, витягувати, резервний варіант, розділяти

Ось та сама підготовка отримання після переходу. Консольний приклад у parse-docx-to-markdown-chunks-dotnet записує інформацію, Markdown, текст PDF та чотири частини заголовків для постаченого DOCX.

Крок 1 — Перевірка за допомогою GetDocumentInfo

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

using var parser = new Parser(sourcePath);
var info = parser.GetDocumentInfo();

var sb = new StringBuilder();
sb.AppendLine($"Source: {Path.GetFileName(sourcePath)}");
sb.AppendLine($"File type: {info?.FileType}");
sb.AppendLine($"Pages: {info?.PageCount}");
sb.AppendLine($"Size: {info?.Size} bytes");
File.WriteAllText(outputPath, sb.ToString());

Запишіть ці факти поруч з ідентифікатором завдання. У зразку DOCX ви повинні бачити Microsoft Word Open XML Document, 3 сторінки та 14820 байт.

Крок 2 — Надати перевагу Markdown з GetFormattedText

Для Word та форматів, які підтримують форматований витяг, зберігайте ATX‑заголовки для розділювача.

using var parser = new Parser(sourcePath);
using TextReader? reader = parser.GetFormattedText(
    new FormattedTextOptions(FormattedTextMode.Markdown));

if (reader == null)
{
    return null;
}

string markdown = reader.ReadToEnd();
File.WriteAllText(outputPath, markdown, Encoding.UTF8);
return markdown;

Null — це сигнал переключитися на інший шлях, а не збій. Записуйте у UTF-8, щоб інструменти розбиття та diff у git залишалися читабельними.

Крок 3 — Резервний варіант з GetText для PDF

Коли режим Markdown повертає null, залишайте файл у конвеєрі з простим текстовим шаром.

using var parser = new Parser(sourcePath);
using TextReader? reader = parser.GetText();
if (reader == null)
{
    return null;
}

string text = reader.ReadToEnd();
File.WriteAllText(outputPath, text, Encoding.UTF8);
return text;

Очікуйте .txt, а не вигадані заголовки #. Приклад хосту записує Result/sample-pdf.txt для гілки PDF.

Крок 4 — Розбити Markdown за # та

Пройдіться по рядках; завершіть, коли з’явиться новий ATX # або ##; запишіть chunk-NN.md.

var chunks = new List<string>();
var current = new StringBuilder();
using (var reader = new StringReader(markdown))
{
    string? line;
    while ((line = reader.ReadLine()) != null)
    {
        bool isHeading = line.StartsWith("# ", StringComparison.Ordinal)
            || line.StartsWith("## ", StringComparison.Ordinal);
        if (isHeading && current.Length > 0)
        {
            chunks.Add(current.ToString().TrimEnd());
            current.Clear();
        }
        current.AppendLine(line);
    }
}

Спочатку видаліть застарілі файли chunk-*.md. Глибші рядки ### залишаються всередині поточного розділу. Зразок DOCX створює чотири файли у Result/chunks/.

Коли слід замінити вбудовування цілого файлу цим шляхом?

Замінюйте вбудовування цілого файлу, коли вхід змішує Word DOCX та PDF, а індекс потребує Markdown розміром з розділ. Спочатку виконайте GetDocumentInfo, спробуйте Markdown для Office‑файлів, викличте GetText, коли форматований читач повертає null, і розбивайте лише коли існують маркери # / ##. Залиште неструктурований текст PDF без змін, доки не додасте окрему політику розбиття. Такий порядок відповідає прикладу консольного застосунку та робить режими помилок явними для операторів.

Що змінилося для операторів

До Після
Одне вбудовування на файл Одне вбудовування на блок заголовка
Власний Open XML Markdown GetFormattedText режим Markdown
PDF відкидається, коли форматований читач повертає null GetText записує текстовий артефакт
Немає простого попереднього контролю Працює перевірка кількості сторінок та розміру

Вивільняйте кожен Parser та TextReader. Застосуйте ліцензію або temporary license перед нічним обсягом. Ставтеся до завантажень як до ненадійного вводу та парсуйте в ізольованому воркері.

Спробуйте це на власних файлах

Клонуйте parse-docx-to-markdown-chunks-dotnet, відновіть GroupDocs.Parser 26.10.0 та запустіть проти documents/sample.docx. Потім застосуйте ті ж чотири методи до одного зі своїх посібників і відкрийте згенеровані файли chunk-*.md.