💡 مثال کامل قابل اجرا در GitHub موجود است:
parse-docx-to-markdown-chunks-dotnet

روش قدیمی دردناک بود

تقسیم‌بندی مبتنی بر سرعنوان در Markdown یک مسیر آماده‌سازی بازیابی در .NET است که فایل‌های Office و PDF را قبل از تعبیه به متن‌های به‌اندازه بخش تبدیل می‌کند. بدون این کار، تیم‌ها اغلب یک فایل Word کامل را به یک بردار واحد می‌اندازند. پاراگراف پوشش و بند وظیفه یک تعبیه مشترک دارند، بنابراین جستجوی کسینوسی بخش نادرست را رتبه‌بندی می‌کند. من این مشکل را در یک راهنمای داخلی سه‌صفحه‌ای تجربه کردم: هر پرس‌وجو ابتدا بلوک عنوان را برمی‌گرداند چون ایندکس هرگز ساختار # / ## که نویسندگان نوشته بودند را ندیده بود.

ساخت یک مبدل DOCX‑to‑Markdown به‌صورت دستی به معنای عبور از سبک‌های Open XML، نگاشت سطوح لیست و همچنان شکست در زمانی است که بارگذاری بعدی یک PDF باشد که مسیر Markdown قالب‌بندی‌شده‌اش مقدار null برمی‌گرداند. پنجره‌های توکنی با اندازه ثابت این سرعنوان‌ها را نادیده می‌گیرند. بنابراین مجموعه داده‌های شبانه‌روزی مخلوطی از پوسته‌های خالی و کتابچه‌های عظیم می‌شوند چون هیچ‌کس ابتدا بر اساس تعداد صفحه یا اندازه فیلتر نکرده است.

هزینه واقعی تعبیه‌های کل‑فایل: پاسخ‌های نادرست بخش، هزینه‌های استخراج بی‌فایده برای بارگذاری‌های نامناسب، و عدم وجود artefactهای 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 (مجوز موقت)
  • یک فایل Word DOCX (نمونه ۳ صفحه / ۱۴۸۲۰ بایت) و به‌اختیار یک PDF
dotnet add package GroupDocs.Parser --version 26.10.0

روش جدید: بازرسی، استخراج، بازگشت، تقسیم

در اینجا همان آماده‌سازی بازیابی پس از تغییر مسیر نشان داده شده است. نمونهٔ کنسولی تحت parse-docx-to-markdown-chunks-dotnet اطلاعات، Markdown، متن PDF و چهار بخش سرعنوان برای DOCX ارسال‌شده را می‌نویسد.

گام ۱ - بررسی با 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، ۳ صفحه و ۱۴۸۲۰ بایت ببینید.

گام ۲ - ترجیح دادن 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 بنویسید تا ابزارهای chunk و diffهای git قابل خواندن بمانند.

گام ۳ - بازگشت با 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 می‌نویسد.

گام ۴ - تقسیم 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 را اجرا کنید، برای فایل‌های Office Markdown را امتحان کنید، وقتی خوانندهٔ قالب‌بندی‌شده مقدار null دارد GetText را فراخوانی کنید و فقط زمانی که نشانگرهای # / ## وجود داشته باشد تقسیم کنید. متن PDF بدون ساختار را تا زمانی که سیاست chunk جداگانه‌ای اضافه نکنید، دست‌نخورده بگذارید. این ترتیب با برنامهٔ کنسولی نمونه مطابقت دارد و حالت‌های شکست را برای اپراتورها واضح می‌کند.

چه تغییراتی برای اپراتورها ایجاد شد

قبل بعد
یک تعبیه برای هر فایل یک تعبیه برای هر بلوک سرعنوان
Markdown سفارشی Open XML حالت Markdown GetFormattedText
PDF حذف می‌شد وقتی خوانندهٔ قالب‌بندی‌شده null بود GetText یک artefact متنی می‌نویسد
عدم بررسی پیش‌هزینه بررسی تعداد صفحه و اندازه به‌عنوان گیت

هر Parser و TextReader را آزاد کنید. قبل از حجم‌کاری شبانه‌روزی یک مجوز یا مجوز موقت اعمال کنید. بارگذاری‌ها را به‌عنوان ورودی نامطمئن در نظر بگیرید و در یک کارگر ایزوله تجزیه کنید.

آن را روی فایل‌های خود امتحان کنید

مخزن parse-docx-to-markdown-chunks-dotnet را کلون کنید، GroupDocs.Parser 26.10.0 را بازگردانید و در مقابل documents/sample.docx اجرا کنید. سپس همان چهار روش را روی یکی از کتابچه‌های خود اعمال کنید و فایل‌های تولید شدهٔ chunk-*.md را باز کنید.