عنوان:

‫معرفی InterpolatedParser: جایگزینی خواناتر برای Regex در سی شارپ


نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۰۲/۰۷ ۰۸:۵۰
آدرس: www.dntips.ir
این مطلب به معرفی کتابخانه InterpolatedParser در زبان برنامه‌نویسی سی شارپ می‌پردازد که به عنوان یک جایگزین با قابلیت خوانایی بالاتر برای عبارات با قاعده (Regular Expressions یا Regex) در تجزیه متون ساختاریافته مطرح شده است. در ادامه، ضمن بررسی چیستی و نحوه عملکرد این کتابخانه، مزایا و محدودیت‌های آن در مقایسه با Regex و موارد کاربرد مناسب آن تشریح می‌گردد.

InterpolatedParser چیست؟

InterpolatedParser یک کتابخانه برای .NET است که امکان تجزیه معکوس رشته‌ها با استفاده از نحو درونیابی رشته‌ای (String Interpolation) را فراهم می‌کند. برخلاف استفاده معمول از درونیابی رشته‌ای برای ایجاد رشته‌ها با استفاده از مکان‌های نگهدارنده (Placeholders)، این کتابخانه رشته‌ها را با استفاده از همان نحو به متغیرها تجزیه می‌کند. این رویکرد برای متونی که دارای ساختار قابل پیش‌بینی هستند بسیار کارآمد است.
برای نمونه، قطعه کد زیر نحوه عملکرد InterpolatedParser را نشان می‌دهد:
string name = "";
int age = 0;
InterpolatedParser.Parse($"Name: {name}, Age: {age}", "Name: Alice, Age: 30");

Console.WriteLine(name); // خروجی: Alice
Console.WriteLine(age);  // خروجی: 30
در این مثال، الگوی رشته‌ای ($"Name: {name}, Age: {age}") ساختار مورد انتظار را تعریف می‌کند، در حالی که رشته ورودی ("Name: Alice, Age: 30") مقادیر واقعی را ارائه می‌دهد. متد Parse از کلاس InterpolatedParser با استفاده از این الگو، مقادیر موجود در رشته ورودی را به متغیرهای name و age نسبت می‌دهد.

مزایای InterpolatedParser

استفاده از InterpolatedParser مزایای متعددی را به همراه دارد:
  • ایمنی نوع (Type Safety): مقادیر مستقیماً به نوع صحیح متغیرها (مانند int، DateTime) تجزیه می‌شوند، که احتمال بروز خطاهای مربوط به نوع داده را کاهش می‌دهد.
  • الگوهای خوانا (Readable Templates): الگوهای مورد استفاده برای تجزیه، شباهت زیادی به فرمت خروجی مورد انتظار دارند، که درک آن‌ها را در مقایسه با عبارات پیچیده Regex آسان‌تر می‌کند.
  • ادغام آسان (Easy Integration): این کتابخانه به خوبی با انواع رایج .NET و همچنین انواع سفارشی که رابط‌های IParseable<T> یا ISpanParseable<T> را پیاده‌سازی می‌کنند، سازگار است.

مقایسه InterpolatedParser و Regex: چه زمانی از کدام استفاده کنیم؟

عبارات با قاعده (Regex) ابزاری قدرتمند برای دستکاری متن هستند، اما می‌توانند پیچیده و مستعد خطا باشند. InterpolatedParser یک جایگزین ساده‌تر برای متون ساختاریافته ارائه می‌دهد، اما فاقد انعطاف‌پذیری Regex برای الگوهای پیچیده است.
در ادامه، نمونه‌هایی از کاربرد هر دو روش در کنار یکدیگر آورده شده است:
تجزیه لاگ (Log Parsing):
  • InterpolatedParser:
string level = "";
string message = "";
InterpolatedParser.Parse($"[{level}] {message}", "[INFO] Application started");
  • Regex:
var match = Regex.Match("[INFO] Application started", @"\[(.*?)\] (.+)");
string level = match.Groups[1].Value;
string message = match.Groups[2].Value;

خطوط پیکربندی (Config Lines) (key=value):
  • InterpolatedParser:
string key = "";
string value = "";
InterpolatedParser.Parse($"{key}={value}", "username=admin");
  • Regex:
var match = Regex.Match("username=admin", @"(.+?)=(.+)");
string key = match.Groups[1].Value;
string value = match.Groups[2].Value;

اعتبارسنجی ایمیل (Email Validation) (فقط Regex):
bool isValid = Regex.IsMatch("user@example.com", @"^[^@\s]+@[^@\s]+\.[^@\s]+$");

الگوهای پیچیده (URLs با گروه‌های اختیاری) (فقط Regex):
var match = Regex.Match("https://example.com/path", @"^(https?)://([^/]+)(/.*)?$");

همانطور که در مثال‌ها مشاهده می‌شود، InterpolatedParser برای ساختارهای ساده و قابل پیش‌بینی، راه حلی خواناتر ارائه می‌دهد، در حالی که Regex برای الگوهای پیچیده‌تر و مواردی مانند اعتبارسنجی که نیاز به تعریف دقیق‌تری از ساختار دارند، مناسب‌تر است.

چه زمانی از InterpolatedParser استفاده کنیم؟

InterpolatedParser در سناریوهایی که با فرمت‌های قابل پیش‌بینی و متون ساختاریافته سروکار داریم، بسیار کارآمد است:
  • لاگ‌های ساختاریافته (مانند "[INFO] Application started")
  • گزارش‌ها یا فرم‌ها (مانند "Name: John Doe, Age: 30")
  • سطرهای ساده فایل‌های CSV (مقادیر با کاما جدا شده) (مانند "Alice,30,Female")
  • رویدادهای بازی یا داده‌های تله‌متری (مانند "Event: Jump, PlayerID: 123")
  • دستورات ربات‌های گفتگو (مانند "/ban @user123 reason=spam")
  • فایل‌های پیکربندی (مانند "key=value")

چه زمانی از InterpolatedParser استفاده نکنیم؟

از InterpolatedParser در موارد زیر اجتناب کنید:
  • ورودی‌های با فرمت آزاد یا ناسازگار
  • وظایف اعتبارسنجی (مانند ایمیل‌ها، گذرواژه‌ها)
  • الگوهای اختیاری یا تکرارشونده
  • تجزیه HTML، JSON یا XML (برای این موارد، کتابخانه‌های تخصصی مانند System.Text.Json یا System.Xml.Linq مناسب‌تر هستند.)
  • مسیرهای حیاتی از نظر عملکرد که نیاز به تجزیه سنگین دارند (Regex در برخی موارد می‌تواند از نظر عملکرد بهینه‌تر باشد.)

موارد استفاده واقعی (قطعه‌های کد)

در این بخش، نمونه‌هایی از کاربرد InterpolatedParser در سناریوهای واقعی ارائه شده است:
دستورات ربات گفتگو:
string command = "";
string user = "";
InterpolatedParser.Parse($"/{command} @{user}", "/ban @user123");

تجزیه پیام لاگ:
string level = "";
string message = "";
InterpolatedParser.Parse($"[{level}] {message}", "[ERROR] Disk full");

استخراج اطلاعات نسخه:
int major = 0, minor = 0;
InterpolatedParser.Parse($"Version {major}.{minor}", "Version 1.2");

تجزیه خطوط چند مقداری:
int cpuUsage = 0, ramUsage = 0;
InterpolatedParser.Parse($"CPU: {cpuUsage}%, RAM: {ramUsage}%", "CPU: 40%, RAM: 80%");

تجزیه داده‌های تله‌متری دستگاه‌های IoT با انواع سفارشی

در این بخش، یک سناریو واقعی که در آن InterpolatedParser بسیار مفید واقع می‌شود، بررسی می‌گردد. فرض کنید یک دستگاه IoT (Internet of Things) برای کشاورزی هوشمند، بسته‌های داده‌های تله‌متری ساختاریافته‌ای را با فرمت زیر ارسال می‌کند:
[TIMESTAMP] DeviceID|SoilMoisture|Temperature|Uptime|ErrorCodes
یک نمونه از ورودی خام می‌تواند به شکل زیر باشد:
"[2025-04-12T11:30:00Z] AGRI-007|45.6|28.3|15d 4h 30m|ERR_001,ERR_003"
برای تجزیه این نوع داده‌ها با InterpolatedParser، می‌توان از انواع سفارشی (Custom Types) که رابط IParseable<T> را پیاده‌سازی می‌کنند، استفاده نمود.
پیاده‌سازی:
// نوع سفارشی برای تجزیه زمان روشن بودن دستگاه (uptime)
public struct DeviceUptime : IParseable<DeviceUptime>
{
    public TimeSpan Value { get; set; }

    public static DeviceUptime Parse(string s, IFormatProvider? provider)
    {
        var parts = s.Split(' ');
        int days = int.Parse(parts[0].TrimEnd('d'));
        int hours = int.Parse(parts[1].TrimEnd('h'));
        int minutes = int.Parse(parts[2].TrimEnd('m'));
        return new DeviceUptime { Value = new TimeSpan(days, hours, minutes, 0) };
    }
}

// نوع سفارشی برای تجزیه لیست کدهای خطا (error codes)
public struct ErrorCodeList : IParseable<ErrorCodeList>
{
    public List<string> Codes { get; set; }

    public static ErrorCodeList Parse(string s, IFormatProvider? provider)
    {
        return new ErrorCodeList { Codes = s.Split(',').ToList() };
    }
}

// اجرای تجزیه
DateTime timestamp = DateTime.MinValue;
string deviceId = "";
double soilMoisture = 0;
double temperature = 0;
DeviceUptime uptime = new();
ErrorCodeList errorCodes = new();

string input = "[2025-04-12T11:30:00Z] AGRI-007|45.6|28.3|15d 4h 30m|ERR_001,ERR_003";

InterpolatedParser.Parse(
    $"[{timestamp}] {deviceId}|{soilMoisture}|{temperature}|{uptime}|{errorCodes}",
    input
);

// نتایج:
// timestamp = 2025-04-12 11:30:00 UTC
// deviceId = "AGRI-007"
// soilMoisture = 45.6
// temperature = 28.3
// uptime.Value = 15 روز، 4 ساعت، 30 دقیقه
// errorCodes.Codes = ["ERR_001", "ERR_003"]
در این مثال، انواع سفارشی DeviceUptime و ErrorCodeList نحوه تجزیه رشته‌های خاص موجود در داده‌های تله‌متری را تعریف می‌کنند. DeviceUptime رشته‌ای مانند "15d 4h 30m" را به یک شیء از نوع TimeSpan تبدیل می‌کند، و ErrorCodeList رشته‌ای با کدهای خطا جدا شده با کاما را به یک لیست از نوع List<string> تبدیل می‌نماید.

آنچه در این مثال مشاهده می‌کنیم:
  • مدیریت انواع سفارشی: نشان می‌دهد که چگونه می‌توان با پیاده‌سازی رابط IParseable<T>، منطق تجزیه سفارشی را برای انواع داده‌های خاص تعریف کرد.
  • ساختار پیچیده: InterpolatedParser قادر است چندین نوع داده مختلف را در یک عملیات تجزیه واحد مدیریت کند.
  • الگوی واقعی: فرمت داده‌های تله‌متری شبیه‌سازی شده، یک الگوی رایج در برنامه‌های کاربردی IoT است. InterpolatedParser در این حالت با حفظ ایمنی نوع، خوانایی الگو و تخصیص مستقیم متغیرها، عملکرد خوبی ارائه می‌دهد.
  • قابلیت توسعه‌پذیری: این مثال نشان می‌دهد که چگونه می‌توان IParseable<T> را برای فرمت‌های خاص دامنه پیاده‌سازی کرد.

در مقایسه با استفاده از Regex برای این سناریو، که نیازمند الگوهای پیچیده و تبدیل دستی انواع داده‌ها خواهد بود، InterpolatedParser با رویکرد اعلانی خود، خوانایی و ایمنی نوع را حفظ می‌کند.

چگونه شروع کنیم؟

برای استفاده از InterpolatedParser:
  1. نصب از طریق NuGet: از طریق مدیریت بسته NuGet در ویژوال استودیو یا با استفاده از دستور dotnet add package InterpolatedParser در خط فرمان، این بسته را به پروژه خود اضافه کنید.
  2. انواع پشتیبانی شده: InterpolatedParser از انواع داخلی مانند int، DateTime و مجموعه‌ها (List<T>, آرایه‌ها) پشتیبانی می‌کند. همچنین، از انواع سفارشی که رابط‌های IParseable<T> یا ISpanParseable<T> را پیاده‌سازی می‌کنند نیز پشتیبانی می‌شود.
  3. محدودیت‌ها: همانطور که قبلاً ذکر شد، این کتابخانه برای مسیرهای حیاتی از نظر عملکرد و الگوهای بسیار پیچیده به اندازه Regex مناسب نیست.

برای اطلاعات بیشتر، می‌توانید به مخزن رسمی GitHub این کتابخانه مراجعه کنید.

نتیجه‌گیری

InterpolatedParser جایگزینی برای Regex نیست، بلکه یک ابزار مکمل برای وظایف تجزیه خوانا و ساختاریافته است. ایمنی نوع و نحو بصری آن، این کتابخانه را به یک راه حل کارآمد برای فرمت‌های قابل پیش‌بینی مانند لاگ‌ها یا گزارش‌ها تبدیل می‌کند.
نکته پایانی: اگر فرمت خروجی داده‌ها تحت کنترل شما باشد، می‌توانید با استفاده از InterpolatedParser به راحتی آن‌ها را تجزیه کنید.


مشاهده مطلب اصلی