تشخیص URL ها در متن

چهارشنبه 23 تیر 1395

در این مقاله به شما آموزش داده می شود که چگونه با استفاده از الگوی Regex ، بتوانید URL های موجود در یک متن را پیدا کنید.

تشخیص URL ها در متن

اگر شما نیاز داشته باشید تا URL ها را در یک متن پیدا کنید، احتمالا برای پیدا کردن راه حل، به جستجوی صفحات وب خواهید پرداخت. اگر صفحات وب را جستجو کنید، مقالات و مثال های زیادی پیدا خواهید کرد که به شما نحوه به کارگیری الگوی Regex برای یافتن URL ها را نشان می دهند.

URL های معتبر ممکن است حاوی رنج وسیعی از کاراکترها باشند که از قوانین مشخصی در دامنه، query ، path تبعیت می کنند.به عنوان یک نتیجه، یک الگوی Regex  برای تطابق URL  ها طولانی و پیچیده است. الگوهایی که برای این کار وجود دارند، از 20 کاراکتر تا 2000 کاراکتر و حتی بیشتر اندازه دارند. بجز طول زیاد و پیچیدگی این الگوها، هیچ کدام از آن ها URL های معتبر را شناسایی نمی کنند.

برخی الگوهایی که برای نمونه در اینترنت قرار گرفته اند، برخی قسمت های آن ها حذف شده اند، مثل : http://localhost ، حذف  و ویرایش این الگوها نیازمند انجام مهندسی معکوس است. یکی از مهم ترین نکات برای انجام دادن عملیات بر روی URL ها ، پیدا کردن نقطه ی پایان آن ها است.

به عنوان مثال، URL ها ممکن است شامل پرانتز نیز باشند، این روش نوشتن URL در Wikipedia متداول است و افراد نیز عموما عادت دارند که URL ها را درون پرانتز قرار می دهند. برای مثال : "(http://www.example.com)" .

در متن برای شناسایی URL ها نیاز به پردازش داریم. معمولا URL ها با علامت های نشانه گذاری همراه هستندو مثل: "Is it http://example.com?"   علامت های نشانه گذاری در URL ها معتبر هستند و استفاده از آن ها موردی ندارد. این علائم شامل علامت های نشانه گذاری مانند ویرگول، پرانتز ها و علامت تعجب هستند. اما وقتی این نشانه گذاری ها با یک علامت فاصله ی خالی همراه می شوند و یا در پایان جمله قرار می گیرند، معمولا قسمتی از  URL ها محسوب نمی شوند. اکثر الگوها نیز این موارد را جزو شمارش URL به حساب نمی آورند.

روشی که در زیر برای این کار از آن استفاده خواهیم کرد، روش متفاوتی است.

در این روش ، ما بر روی متن تمرکز می کنیم و یک سری قوانین ساده برای این روش تعیین می کنیم:

1-همه ی URL ها باید با http:// ، https:// ، ftp:// شروع شوند.

2-یک URL باید در درون یک پرانتز قرار داشته باشد.

3-یک URL باید با علامت های متداول نشانه گذاری به پایان برسد.

4-یک URL باید در انتها یک فاصله ی خالی داشته باشد.

این قوانین، همه ی URL ها را شناسایی نمی کنند(false negatives) و URL های نامعتبر را می شناسند. (false positives) برای برنامه ای که ما قصد ساخت آن را داریم، می توانیم این میزان خطای برنامه را بپذیریم و مشکلی برای کار ما به وجود نخواهد آمد.

فهم کد

ReplaceUrls() یک مجموعه ی string  است که یک متد delegate  قبول می کند، URL ها را پیدا می کند اما فراهم کردن قسمت های جایگزینی متن را بر عهده ی delegate می گذارد. معمولا ، delegate برای پیوند دادن URLآن را در داخل یک HTML  قرار می دهد. Delegate ممکن است بر اساس منطق خودش تصمیم بگیرد که URL را بدون تغییر به ما برگرداند.

ReplaceUrls() از یک Regex  ساده برای پیدا کردن نقطه ی شروع URL ها استفاده می کند و برای تشخیص انتهای URL  نیز از پرانتز ها استفاده می کند.

متد GetUrlDelimiter() متن را بررسی می کند تا ببیند آیا در داخل پرانتز قرار دارد یا نه . سایر مواردی که می توانند به جای پرانتز مورد استفاده قرار بگیرند، []  و <<>> و "" و ‘’ هستند . اگر یک URL با یکی از این موارد شروع شود، ReplaceUrls() از FindEndOfDelimitedUrl() استفاده می کند تا کاراکتر مناسب را پیدا کند. در غیر اینصورت، ReplaceUrls() از FindEndOfUrl() برای یافتن انتهای URL استفاده می کند.

کدنویسی

در اینجا مثالی استفاده از ReplaceUrls() آورده شده است:

text = text.ReplaceUrls(LinkifyUrl);
...
public string LinkifyUrl(string url) {
    return String.Format("<a href=\"{0}\">{0}</a>", url);
{

کد کامل برنامه

using System.Text;
using System.Text.RegularExpressions;

namespace JohnCardinal.Html {
   public delegate string UrlEvaluator(string url);

   internal static class UrlExtensions {
      private const char kLeftPointingDoubleAngle = '\u00AB';
      private const char kRightPointingDoubleAngle = '\u00BB';
      private const char kNoDelimiter = '\0';

      private static Regex UrlPrefix = new Regex("(https?|ftp)://",
            RegexOptions.IgnoreCase | RegexOptions.Compiled);

      public static string ReplaceUrls(this string text, UrlEvaluator evaluator) {
         var matches = UrlPrefix.Matches(text);
         if (matches.Count == 0) {
            return text;
         }

         int copied = 0;
         var sb = new StringBuilder();

         foreach(Match match in matches) {
            if (match.Index > copied) {
               sb.Append(text, copied, match.Index - copied);
            }

            var delimiter = GetUrlDelimiter(text, match);
            var end = (delimiter == kNoDelimiter) ?
                  FindEndOfUrl(text, match) :
                  FindEndOfDelimitedUrl(text, match, delimiter);

            var url = text.Substring(match.Index, end - match.Index + 1);
            if (url.Length > match.Length) {
               sb.Append(evaluator(url));
            }
            else {
               sb.Append(url);
            }

            copied = end + 1;
         }

         if (text.Length > copied) {
            sb.Append(text, copied, text.Length - copied);
         }

         return sb.ToString();
      }

      private static int FindEndOfUrl(string text, Match match) {
         const string kWhitespace = " \r\n\t";

         var index = match.Index;
         while (index < text.Length) {
            switch (text[index]) {
               case ' ':
               case '\r':
               case '\n':
               case '\t':
                  // whitespace ends the URL
                  return index - 1;

               case '.':
               case ',':
               case '!':
               case '?':
               case ':':
               case ';':
                  // common punctuation followed by whitespace
                  // ends the URL
                  if (index < text.Length - 1) {
                     if (kWhitespace.IndexOf(text[index + 1]) != -1) {
                        return index - 1;
                     }
                  }
                  // common punctuation at the end of the text
                  // ends the URL
                  else if (index == text.Length - 1) {
                     return index - 1;
                  }
                  break;

            }
            index++;
         }
         return index - 1;
      }

      private static int FindEndOfDelimitedUrl(string text, Match match, char delimiter) {
         var nested = 1;

         var index = match.Index;
         while (index < text.Length) {
            switch (text[index]) {
               case ' ':
               case '\r':
               case '\n':
               case '\t':
                  // whitespace ends the URL
                  return index - 1;

               case '"':
                  if (delimiter == '"') {
                     return index - 1;
                  }
                  break;

               case '\'':
                  if (delimiter == '\'') {
                     return index - 1;
                  }
                  break;

               case '(':
                  if (delimiter == '(') nested++;
                  break;

               case ')':
                  if (delimiter == '(') {
                     nested--;
                     if (nested == 0) {
                        return index - 1;
                     }
                  }
                  break;

               case kRightPointingDoubleAngle:
                  if (delimiter == kLeftPointingDoubleAngle) {
                     return index - 1;
                  }
                  break;

               case ']':
                  if (delimiter == '[') {
                     return index - 1;
                  }
                  break;
            }
            index++;
         }

         return index - 1;
      }

      private static char GetUrlDelimiter(string text, Match match) {
         const string kDelimiters = "\"'([\u00AB";

         if (match.Index > 0) {
            var index = match.Index - 1;

            if (kDelimiters.IndexOf(text[index]) != -1) {
               return text[index];
            }
         }
         return kNoDelimiter;
      }
   }
}

امیدوارم این مقاله برای شما مفید بوده باشد.

آموزش سی شارپ

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان
  • C#.net
  • 1k بازدید
  • 1 تشکر

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید