💡 Полный рабочий пример доступен на GitHub:
parse-docx-to-markdown-chunks-dotnet
Старый способ был болезненным
Разделение Markdown по заголовкам — это путь подготовки извлечения в .NET, который преобразует файлы Office и PDF в текст размером с раздел перед встраиванием. Без этого команды часто загружают весь файл Word в один вектор. Вводный абзац и пункт обязательств делят одно встраивание, поэтому поиск по косинусу ранжирует неправильный раздел. Я столкнулся с этим в трехстраничном внутреннем руководстве: каждый запрос сначала вытягивал блок заголовка, потому что индекс никогда не видел структуру # / ##, которую уже написали авторы.
Самостоятельная реализация конвертера DOCX-to-Markdown подразумевает обход стилей Open XML, сопоставление уровней списков и всё равно приводит к сбоям, когда следующая загрузка — PDF, путь форматированного Markdown которого возвращает null. Окна фиксированного размера токенов игнорируют эти заголовки. За ночь корпус данных смешивает пустые оболочки с огромными руководствами, потому что никто не проверял количество страниц или размер заранее.
Настоящая цена встраиваний целых файлов: ответы из неправильных разделов, потраченные впустую ресурсы на извлечение из мусорных загрузок и отсутствие артефактов
chunk-01.md, которые операторы могли бы открыть при плохой партии.
Есть лучший способ
GroupDocs.Parser for .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)
- Word DOCX (пример состоит из 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. Примените лицензию или временная лицензия перед ночным объёмом. Рассматривайте загрузки как недоверенный ввод и парсите их в изолированном воркере.
Попробуйте на своих файлах
Клонируйте parse-docx-to-markdown-chunks-dotnet, восстановите GroupDocs.Parser 26.10.0 и запустите против documents/sample.docx. Затем примените те же четыре метода к одному из ваших руководств и откройте сгенерированные файлы chunk-*.md.