طراحی سایت و برنامه نویسی

آموزش طراحی سایت و برنامه نویسی

طراحی سایت و برنامه نویسی

آموزش طراحی سایت و برنامه نویسی

مرتب سازی ادغامی (Merge Sort) در جاوا — به زبان ساده

در این راهنما نگاهی به الگوریتم مرتب سازی ادغامی و پیاده‌سازی آن در جاوا خواهیم داشت. مرتب‌سازی ادغامی یکی از مؤثرترین تکنیک‌های مرتب‌سازی بر مبنای پارادایم «تقسیم و حل» (divide and conquer) است.

الگوریتم مرتب سازی ادغامی

مرتب‌سازی ادغامی یک الگوریتم «تقسیم و حل» است که در آن ابتدا مسئله به مسائل فرعی تقسیم می‌شود. زمانی که راه‌حل‌ها برای مسائل فرعی آماده شد، مجدداً آن‌ها را با هم ترکیب می‌کنیم تا راه‌حل نهایی برای مسئله اصلی به دست آید.

این یکی از الگوریتم‌هایی است که با استفاده از «بازگشت» (recursion) به سادگی پیاده‌سازی می‌شود، چون به جای مسئله اصلی با مسائل فرعی سر و کار داریم.

الگوریتم آن را می‌توان به صورت فرایند 2 مرحله‌ای زیر توصیف کرد:

  • تقسیم: در این مرحله آرایه ورودی به دو نیمه تقسیم می‌شود. محور تقسیم نقطه میانی آرایه است. این مرحله به صورت بازگشتی روی همه آرایه‌های نیمه انجام می‌یابد تا این که دیگر نیمه آرایه‌ای برای تقسیم وجود نداشته باشد.
  • حل: در این مرحله باید آرایه‌های تقسیم‌شده را مرتب‌سازی و ادغام کنیم و این کار از بخش زیرین به سمت بالا برای به دست آوردن آرایه مرتب انجام می‌یابد.

نمودار زیر فرایند کامل مرتب‌سازی ادغامی را برای آرایه نمونه {10, 6, 8, 5, 7, 3, 4} نمایش می‌دهد.

اگر نگاهی دقیق‌تر به الگوریتم داشته باشیم، می‌بینیم که آرایه به صورت بازگشتی به نیمه‌های متوالی تقسیم می‌شود تا این که اندازه آن برابر با 1 شود. زمانی که اندازه آن برابر با 1 شد، فرایندهای ادغام وارد عمل می‌شوند و شروع به ادغام آرایه‌ها برای رسیدن به آرایه مرتب می‌کنند.

مرتب سازی ادغامی

پیاده‌سازی

برای پیاده‌سازی الگوریتم فوق، یک تابع مرتب‌سازی ادغامی می‌نویسیم که یک آرایه ورودی و طولانی را به عنوان پارامتر می‌گیرد. این یک تابع بازگشتی خواهد بود، بنابراین به یک شرایط پایه و بازگشت نیاز داریم.

شرایط پایه در صورتی بررسی می‌شوند که طول آرایه برابر با 1 باشد، و فقط در این حالت بازگشت می‌یابد. در بقیه موارد فراخوانی بازگشتی اجرا خواهد شد.

برای حالت بازگشتی اندیس میانه را پیدا کرده و دو آرایه موقت []l و []r را ایجاد می‌کنیم. سپس تابع mergeSort به صورت بازگشتی برای هر دو آرایه فرعی فراخوانی می‌شود:

بعد تابع merge را فراخوانی می‌کنیم که در ورودی خود هر دو آرایه فرعی و اندیس‌های آغاز و پایان هر دو آرایه فرعی را می‌گیرد. تابع merge عناصر هر دو آرایه فرعی را یک به یک مقایسه می‌کند و عنصر کوچک‌تر را درون آرایه ورودی قرار می‌دهد.

زمانی که به انتهای یک آرایه فرعی برسید، بقیه عناصر از آرایه دیگر درون آرایه کپی می‌شوند و بدین ترتیب آرایه مرتب‌شده نهایی به دست می‌آید:

تست unit برنامه به صورت زیر است:

پیچیدگی

از آنجا که مرتب‌سازی ادغامی یک الگوریتم بازگشتی است، پیچیدگی زمانی آن را می‌توان به صورت رابطه بازگشتی زیر بیان کرد:

(2T(n/2 متناظر با زمان مورد نیاز برای مرتب‌سازی آرایه‌های فرعی و زمان (O(n برای ادغام آرایه کلی است. زمانی که مسئله حل شود، پیچیدگی زمانی به (O(nLogn می‌رسد.

این پیچیدگی برای بدترین حالت، حالت میانگین و بهترین حالت درست است زیرا همواره آرایه را به دو نیمه تقسیم کرده و سپس ادغام می‌کند. پیچیدگی فضایی الگوریتم نیز (O(n است، چون آرایه‌های موقتی در هر فراخوانی بازگشتی ایجاد می‌شوند.

سخن پایانی


در این راهنمای کوتاه، با طرز کار الگوریتم مرتب‌سازی ادغامی و روش پیاده‌سازی آن در جاوا آشنا شدیم. همه کدهای این راهنما را می‌توانید در این صفحه گیت‌هاب (+) ملاحظه کنید.

منبع: فرادرس

ایجاد نمای سفارشی (Custom View) در اندروید — از صفر تا صد

پلتفرم اندروید بازه وسیعی از ویجت‌های رابط کاربری را ارائه کرده است که برای نیازهای اغلب اپلیکیشن‌ها کافی هستند. این ویجت‌ها عالی هستند و محصولات نهایی کاملاً کاربردی و زیبایی را می‌سازند؛ اما در پاره‌ای موارد توسعه‌دهندگان نرم‌افزار تمایل دارند فراتر از این بیندیشند و رابط‌های سفارشی خاص خود را بسازند. بهترین روش برای پاسخ دادن به این خلاقیت، ساخت نمای سفارشی است.

در این نوشته به تعریف و برسی نماهای سفارشی و سپس بخش جذاب‌تر یعنی نمایش آن‌ها خواهیم پرداخت.

اصطلاحات مهم

در آغاز باید برخی اصطلاح‌های مقدماتی را برای درک بهتر تعریف کنیم.

نمای اندروید (Android View)

نمای اندروید یا Android View یک کلاس مبنا برای ساخت رابط کاربری است که به توسعه‌دهندگان فرصتی برای ایجاد طراحی‌های پیچیده می‌دهد. این نما ناحیه‌ای مستطیلی روی صفحه اشغال می‌کند و مسئول اندازه‌گیری، طرح‌بندی و ترسیم خود در راستای عناصر فرزندش است. به علاوه نماها همه ورودی‌های کاربر را مدیریت می‌کنند.

نمای سفارشی

گروه نما (ViewGroup)

یک گروه نما یا ViewGroup، نمای خاصی است که توانایی گنجاندن نماهای دیگر (فرزندان) را در خود دارد و مشخصات طرح‌بندی خاص خود را تعریف می‌کند. این نما همچنین محلی است که هر نمای فرعی (subview) می‌تواند از آن مشتق شود.

نمای سفارشی (Custom View)

هر نمایی که خارج از ویجت مبنای اندروید ایجاد شود را می‌توان یک نمای سفارشی یا Custom View دانست. در این نوشته کل توجه ما روی این نمای سفارشی است.

روش‌های پیاده‌سازی

روش‌های بسیار مختلفی برای پیاده‌سازی نماهای سفارشی وجود دارند و رویکردی که انتخاب می‌شود به نیازهای شما بستگی دارد. در ادامه برخی روش‌ها را مورد بررسی قرار می‌دهیم:

بسط دادن ویجت اندروید موجود

این روش زمانی که حجم بالایی از کد راه‌اندازی برای نمای شما لازم باشد و بخواهید از آن در جاهای مختلفی استفاده کنید مناسب خواهد بود. برای اجتناب از درج کد شلوغ درون اکتیویتی یا فرگمان، می‌توانید ویجت مبنا را بسط داده و همه کارهای راه‌اندازی را درون سازنده انجام دهید، از این رو می‌توان به سادگی از آن مجدداً استفاده کرد. این روش به طور بدیهی ساده‌ترین رویکرد برای پیاده‌سازی نماهای سفارشی محسوب می‌شود.

بسط دادن نمای مبنای اندروید

اگر می‌خواهید ابتکار به خرج بدهید و همه کارها را از صفر خودتان اجرا کنید، این روش مناسب خواهد بود. در این روش شما همه منطق رفتاری ویجت را خودتان رسم، اندازه‌گیری و برنامه‌ریزی می‌کنید.

گروه‌بندی نماهای موجود با هم

در پاره‌ای اوقات مجموعه‌ای از ویجت‌ها دارید که می‌خواهید آن‌ها را با هم گروه‌بندی کنید تا یک نمای کاملاً جدید ایجاد کنید. برای نمونه فرض کنید یک Textview و یک Button دارید و می‌خواهید آن‌ها را درون یک LinearLayout گروه‌بندی کنید. این نما عموماً به نام «نمای ترکیبی» (Compound View) شناخته می‌شود. مزیت‌های این کار به شرح زیر هستند:

  • یک منطق کپسوله‌سازی شده و متمرکز داریم.
  • می‌توانیم از تکرار شدن کد جلوگیری کنیم.
  • کد قابلیت استفاده مجدد و ماژولار بودن پیدا می‌کند.

اندروید نماها را چگونه رسم می‌کند؟

در این بخش به توضیح روش اندروید برای رسم نماها می‌پردازیم. در آغاز سه مرحله وجود دارند که پیش از نمایش نهایی نما روی صفحه باید اجرا شوند. این سه مرحله، اندازه‌گیری، طرح‌بندی و رسم هستند. هر یک از مراحل به صورت پیمایش «عمق-اول» (depth-first) سلسله‌مراتب نما است که از والد به سمت فرزند حرکت می‌کند. در هر مرحله متدی وجود دارند که می‌تواند بسته به نیازها override شده و تغییر یابد. این فرایند را می‌توان به دو مرحله تقسیم کرد:

  • مرحله اندازه‌گیری و طرح‌بندی
  • مرحله رسم

نمای سفارشی

مرحله اندازه‌گیری و طرح‌بندی

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

نمودار شماره‌گذاری شده زیر شیوه اندازه‌گیری هر نما را در هر گام نمایش می‌دهد:

نمای فرزند اقدام به تعریف LayoutParams به صورت برنامه‌نویسی شده و یا در XML می‌کند و والد این مقادیر را با استفاده از متد ()getLayoutParams بازیابی می‌کند.

والد، MeasureSpecs را محاسبه کرده و آن را با استفاده از ()child.measure در سلسله‌مراتب به سمت پایین ارسال می‌کند. Measurespecs شامل حالت و مقدار است.

سه حالت برای اندازه‌گیری وجود دارند:

  • EXACTLY – یک اندازه دقیق مانند تنظیم عرض/ارتفاع برابر با 50 dp یا match_parent استفاده می‌شود.
  • AT_MOST – والد اندازه بیشینه‌ای که فرزند می‌تواند اتخاذ کند را تعیین می‌کند. این حالتی است که هنگام تعیین عرض/ارتفاع برابر با wrap_content استفاده می‌شود.
  • UNSPECIFIED – اندازه مشخصی وجود ندارد و فرزند در مورد اندازه آزادانه عمل می‌کند.

متد ()onMeasure به همراه پارامترهای MeasureSpecs فراخوانی می‌شود. در این متد View اقدام به محاسبه عرض/ارتفاع مطلوب می‌کند و آن را با استفاده از setMeasuredDimension تنظیم می‌کند. به خاطر داشته باشید که متد setMeasuredDimension باید درون measure فراخوانی چون در غیر این صورت موجب بروز استثنای زمان اجرا می‌شود.

مرحله بعد و آخر مرحله طرح‌بندی است. در این مرحله، والد ()child.layout را فراخوانی کرده و اندازه نهایی و موقعیت فرزند را تعیین می‌کند. زمانی که نمای سفارشی را پیاده‌سازی می‌کنید، در صورتی که نمای شما، نماهای فرعی دیگری داشته باشد، باید تنها متد ()onLayout را override کنید.

در نتیجه فرایند اندازه‌گیری مانند یک مذاکره بین یک والد و فرزند است. فرزند، عرض و ارتفاع مطلوب را محاسبه می‌کند اما والد کسی است که فراخوانی نهایی را برای تنظیم موقعیت و اندازه فرزندش انجام می‌دهد.

 نمای سفارشی

مرحله رسم

آخرین و مهم‌ترین مرحله در رسم نمای سفارشی override کردن متد ()onDraw است. بوم یک کلاس مبنا است که متدهای زیادی برای رسم متن، bitmap-ها، خطوط و دیگر اشکال ابتدایی گرافیکی عرضه کرده است.

هر والد، خود را رسم می‌کند و سپس تقاضا می‌کند که هر فرزند نیز همان کار را انجام دهد. یک اثر جنبی جالب در زمانی است که والد ابتدا خود را رسم می‌کند و در انتها فرزندانش را روی خود رسم کرده و در واقع خود را می‌پوشاند.

ایجاد نمای سفارشی

اکنون نوبت بخشی رسیده است که مدت‌ها منتظر آن بودیم و آن کدنویسی است. در ادامه به بررسی روش ایجاد یک نمای سفارشی با استفاده از «کاتلین» (Kotlin) می‌پردازیم. ما جهت مقاصد آموزشی یک شاخص باتری رسم می‌کنیم تا وضعیت کنونی باتری دستگاه را نمایش دهیم. نمودار زیر وضعیت‌های مختلف یک باتری را نشان می‌دهد:

 نمای سفارشی اندروید

برای ایجاد یک نمای BatteryMeterView باید مراحل زیر را طی کنیم. یک پروژه اندروید استودیو ایجاد کرده و یک کلاس جدید به نام BatteryMeterView اضافه کنید.

آن را با استفاده از کلاس View بسط دهید و سازنده‌ها را طوری اضافه کنید که با super مطابقت داشته باشند.

ما برای آماده‌سازی رسم برخی شیءهای paint، رنگ و شکل‌ها را اعلان می‌کنیم.

View-ی ما همانند یک ویجت ابتدایی باید مقداری آماده‌سازی اولیه داشته باشد و همه مشخصه‌ها نوعی مقدار پیش‌فرض داشته باشند. در ادامه یک شیء همراه درون BatteryMeterView ایجاد کرده و برخی ثابت‌ها را به آن اضافه می‌کنیم.

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

  • عرض و ارتفاع محتوا را تنظیم کنید.
  • اندازه متن‌ مقدار باتری را به اندازه نصف ارتفاع محتوا تنظیم کنید.
  • عرض سر باتری را 1/12 کل عرض باتری تنظیم کنید.
  • موقعیت rect پس‌زمینه را تنظیم کنید.
  • موقعیت rect سر باتری را تنظیم کنید.
  • موقعیت rext سطح باتری را تنظیم کنید.

نکته: با توجه به مقاصد آموزشی این مقاله از برخی مقادیر ثابت برای حاشیه و آفست محتوا استفاده کرده‌ایم.

اینک برای رسم BatteryMeter شروع به override کردن متد ()onDraw می‌کنیم.

  1. پس‌زمینه نما را رسم می‌کنیم.
  2. سر باتری را رسم می‌کنیم.
  3. کانتینری که سطح باتری در آن قرار می‌گیرد را رسم می‌کنیم.
  4. اکنون اگر باتری تغییر پیدا کند، یک لوگوی تغییر نمایش می‌دهیم و در غیر این صورت متن مقدار کنونی باتری را نمایش می‌دهیم.

به خاطر بسپارید که متد onDraw با بسامد 60 بار در ثانیه (fps 60) فراخوانی می‌شود و قرار دادن هر نوع عملیات محاسباتی سنگین و ایجاد شیء درون آن می‌تواند موجب کاهش عملکرد اپلیکیشن شود. برای اجتناب از این وضعیت، می‌توانیم همه اشیا را درون سازنده‌ها بسازیم و در صورت نیاز می‌توانیم مشخصه‌ها را در ادامه تغییر دهیم.

اکنون برای این که به باتری خود امکان تغییر در زمان اجرا بدهیم، باید متد ()invalidate را هر بار که حالت نما به‌روزرسانی می‌شود فراخوانی کنیم. کاری که invalidate انجام می‌دهد این است که به اندروید اجازه می‌دهد بداند که نما خراب شده و نیاز به ترسیم مجدد دارد. لازم به ذکر است که باید مراقب باشید زیرا فراخوانی ()invalidate به تعداد زیاد موجب بروز مشکلاتی می‌شود.

مرحله نهایی، افزودن نمای باتری به Layout است:

به این ترتیب کار به پایان می‌رسد و ما اینک یک باتری سنج داریم که خودمان ایجاد کرده‌ایم. برای مشاهده کد کامل پروژه به این لینک (+) مراجعه کنید.

مزایا و معایب پیاده‌سازی نماهای سفارشی

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

مزایا

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

قابلیت استفاده مجدد/قابلیت خوانایی: زمانی که اپلیکیشن‌های بزرگ‌مقیاس توسعه می‌دهید، قابلیت استفاده مجدد کد همواره موضوعی مورد علاقه است. زمانی که یک نمای سفارشی ایجاد می‌کنید، می‌توانید به سادگی آن را در چند جای دیگر نیز در اپلیکیشن خود مورد استفاده قرار دهید.

عملکرد: در برخی موارد خاص، ساختن نمای سفارشی می‌تواند موجب مقداری بهبود عملکرد شود.

معایب

در این بخش برخی معایب نماهای سفارشی را بررسی می‌کنیم.

زمان و تلاش: ایجاد نماهای سفارشی کاری زمان‌بر است و استفاده از آن قطعاً می‌تواند دشوار باشد تا اینکه به کارکرد آن‌ها آشنا شوید.

پشتکار: چند چیز وجود دارند که در زمان پیاده‌سازی نماهای سفارشی باید آگاه باشید. اول این که باید مطمئن باشید فونت، اندازه متن، رنگ، سایه‌ها، هایلایت و استایل را به درستی مدیریت کرده‌اید. همچنین باید مطمئن شوید که نما روی همه تراکم‌های نمایشی به درستی کار می‌کند چون زیرکلاس بوم اندروید برحسب پیکسل رسم می‌شود و نه DP. اگر با تصاویر کار می‌کنید باید به خاطر داشته باشید که نسبت تصویر، بزرگنمایی و مقیاس‌بندی درستی داشته باشد.

موارد زیادی که باید مدیریت شوند: همچنین باید همه انواع شنونده‌های کلیک، تعامل‌های کاربر، کلیک‌های منفرد، دو بار کلیک، فشردن طولانی، سوایپ کردن و جابجایی را مدیریت کنید.

منبع: فرادرس

ارسال داده های فرم در HTML — راهنمای جامع

در این مقاله به بررسی اتفاقاتی می‌پردازیم که در جریان تحویل یک فرم از سوی کاربر رخ می‌دهد. در این نوشته بررسی خواهیم کرد که در زمان ارسال فرم HTML داده‌های آن به کجا می‌روند و زمانی که به مقصد رسیدند چگونه می‌توانیم آن‌ها را مدیریت کنیم. همچنین به بررسی برخی دغدغه‌های امنیتی مرتبط با ارسال داده های فرم می‌پردازیم. برای مطالعه بخش قبلی این سری مطالب به لینک زیر رجوع کنید:

پیش‌نیاز مطالعه این نوشته سواد مقدماتی رایانه، داشتن درکی از HTML و دانش ابتدایی HTTP و همچنین برنامه‌نویسی سمت سرور است. هدف از این مقاله درک اتفاقاتی است که در زمان ارسال فرم رخ می‌دهد. بدین ترتیب ایده‌ای ابتدایی از شیوه پردازش داده‌ها در سمت سرور به دست می‌آوریم.

داده‌ها به کجا می‌روند؟

در این بخش بررسی می‌کنیم که وقتی دکمه ارسال فرم کلیک می‌شود، داده‌های فرم HTML به کجا می‌روند؟

معماری کلاینت/سرور

مفهوم وب مبتنی بر یک معماری کاملاً ابتدایی کلاینت/سرور است که آن را می‌توان به صورت زیر خلاصه کرد: یک کلاینت (معمولاً مرورگر وب) درخواستی را با استفاده از پروتکل HTTP به سرور (در اغلب موارد یک وب‌سرور مانند Apache، Nginx ،IIS ،Tomcat و غیره) ارسال می‌کند. سرور با استفاده از همان پروتکل به درخواست پاسخ می‌دهد.

ارسال داده

در سمت کلاینت، فرم HTML چیزی به جز روشی کاربرپسند آسان برای پیکربندی یک درخواست HTTP جهت ارسال داده‌ها به سرور نیست. بدین ترتیب کاربر می‌تواند اطلاعاتی را در درخواست HTTP تحویل دهد.

تعریف کردن شیوه ارسال داده‌ها در سمت کلاینت

عنصر <form> شیوه ارسال داده‌ها را تعریف می‌کند. همه خصوصیت‌های آن طوری طراحی شده‌اند که امکان پیکربندی درخواست برای ارسال شدن در زمان کلیک روی دکمه Submit را فراهم سازند. دو مورد از مهم‌ترین خصوصیت‌ها action و method هستند.

خصوصیت action

این خصوصیت شیوه ارسال داده‌ها را تعریف می‌کند. مقدار آن باید یک URL معتبر باشد. اگر این خصوصیت ارائه نشده باشد، داده‌ها به URL صفحه‌ای که شامل فرم است ارسال خواهند شد.

در این مثال، داده‌ها به یک URL مطلق یعنی (http://foo.com) ارسال می‌شوند:

در مثال زیر از یک URL نسبی برای ارسال داده‌ها به URL متفاوتی روی سرور استفاده شده است:

زمانی که مانند مثال زیر، هیچ خصوصیتی ذکر نشود، داده‌های <form> به همان صفحه‌ای ارسال می‌شوند که فرم در آن قرار دارد:

صفحه‌های قدیمی‌تر زیادی از نمادگذاری زیر استفاده می‌کنند تا نشان دهند که داده‌ها باید به همان صفحه‌ای ارسال شوند که شامل فرم است. دلیل الزامی بودن این وضعیت آن است که تا HTML5 خصوصیت action الزامی بود، اما دیگر نیازی به آن ندارید.

نکته: می‌توان از یک URL استفاده کرد که از پروتکل HTTPS یعنی HTTP امن بهره می‌گیرد. زمانی که این کار را انجام دهید، حتی اگر خود فرم روی یک صفحه ناامن میزبانی شده باشد و از طریق HTTP به آن دسترسی داشته باشید، داده‌ها همراه با باقی درخواست رمزنگاری می‌شوند. از سوی دیگر، اگر فرم روی یک صفحه امن میزبانی شده باشد، اما یک URL ناامن به صورت HTTP برای خصوصیت action وارد کنید، همه مرورگرها زمانی که کاربر داده‌ها را ارسال می‌کند، هشداری نمایش می‌دهند، زیرا داده‌ها رمزنگاری نشده‌اند.

خصوصیت method

این خصوصیت شیوه ارسال شدن داده‌ها را تعریف می‌کند. پروتکل HTTP چندین روش برای اجرای درخواست ارائه می‌کند. داده‌های فرم HTML از طریق چند روش مختلف ارسال می‌شوند که رایج‌ترین آن‌ها متد GET و متد POST هستند.

برای درک تفاوت بین دو متد فوق باید گامی به عقب برداریم و طرز کار HTTP را بررسی کنیم. هر بار که می‌خواهید به منبعی روی وب دسترسی پیدا کنید، مرورگر یک درخواست به آن URL ارسال می‌کند. هر درخواست HTTP شامل دو بخش است که یکی هدر و دیگری بدنه درخواست است. بخش هدر شامل مجموعه‌ای از متادیتا کلی در مورد قابلیت‌های مرورگر است و بخش بدنه می‌تواند شامل آن دسته از اطلاعات ضروری باشد که سرور برای پردازش یک درخواست خاص به آن‌ها نیاز دارد.

متد GET

متد GET از سوی مرورگر برای درخواست از سرور جهت بازگشت دادن منبع مفروض مورد استفاده قرار می‌گیرد. در این حالت مرورگر یک بدنه خالی ارسال می‌کند. از آنجا که بدنه درخواست خالی است، اگر فرم با استفاده از این متد ارسال شود، داده‌های ارسالی به سرور به URL الحاق می‌شوند.

فرم زیر را در نظر بگیرید:

از آنجا که متد GET مورد استفاده قرار گرفته است، می‌بینید که در زمان تحویل دادن فرم، URL-ای به صورت زیر در نوار آدرس مرورگر ظاهر می‌شود.

www.foo.com/?say=Hi&to=Mom

ارسال داده

داده‌های الحاقی به URL به صورت یک سری از جفت‌های نام/مقدار هستند. پس از آن که آدرس وب URL پایان یافت یک کاراکتر علامت سؤال (?) و سپس جفت‌های نام/مقدار را می‌آوریم که هر کدام با یک کاراکتر & از هم جدا می‌شوند. در این مورد، ما دو بخش از داده‌ها را به سرور ارسال می‌کنیم:

  • Say که یک مقدار hi دارد.
  • To که یک مقدار Mom دارد.

بدین ترتیب درخواست HTTP به صورت زیر خواهد بود:

متد POST

متد POST کمی متفاوت است. این همان متدی است که مرورگر از آن برای صحبت کردن با سرور هنگام درخواست یک پاسخ استفاده می‌کند. چنین درخواستی با در نظر گرفتن داده‌های ارائه شده در بدنه درخواست HTTP ارسال می‌شود. به زبان ساده درخواست بیان می‌کند: «سلام سرور، به این داده‌ها نگاه کن و یک نتیجه مناسب به من بازگشت بده.» اگر فرم با استفاده از این متد ارسال شود، داده‌ها به بدنه درخواست HTTP الحاق می‌شوند.

زمانی که فرم با استفاده از متد POST ارسال می‌شود، هیچ داده‌ای به URL الحاق نمی‌شود، و درخواست HTTP نیز به صورت زیر به نظر می‌رسد و به جای آن داده‌ها در بدنه درخواست HTTP قرار می‌گیرند:

هدر Content-Length نشان‌دهنده اندازه بدنه است و هدر Content-Type نوع منابعی که به سرور ارسال می‌شود را نشان می‌دهد. در ادامه در مورد این هدرها بیشتر صحبت خواهیم کرد.

مشاهده درخواست‌های HTTP

درخواست‌های HTTP هرگز به کاربر نمایش پیدا نمی‌کنند، اگر می‌خواهید آن‌ها را مشاهده کنید باید از ابزارهایی مانند Network Monitor در فایرفاکس یا Developer Tools در کروم بهره بگیرید. به عنوان مثال، داده‌های فرم به صوت زیر در برگه Network کروم نمایش پیدا می‌کنند. پس از تحویل دادن فرم:

  1. دکمه F12 را بزنید.
  2. گزینه Network را انتخاب کنید.
  3. گزینه All را انتخاب کنید.
  4. در برگه Name گزینه foo.com را انتخاب کنید.
  5. گزینه Headers را انتخاب کنید.

شما می‌توانید داده‌های فرم را به صورتی که در تصویر زیر نمایش یافته به دست آورید:

ارسال داده

تنها چیزی که برای کاربر نمایش پیدا می‌کند، URL فراخوانی‌شده است. چنان که پیش‌تر اشاره کردیم، با یک درخواست GET کاربر داده‌ها را در نوار URL خود می‌بیند، اما با درخواست POST چنین اتفاقی نمی‌افتد. این وضعیت به دو دلیل می‌تواند مهم باشد.

اگر بخواهید یک رمز عبور (یا هر داده حساس دیگر) را ارسال کنید، هرگز نباید از متد GET استفاده کنید، چون در غیر این صورت خطر نمایش یافتن آن در نوار URL وجود دارد که کاری بسیار غیر امن است.

اگر می‌خواهید حجم بالایی از داده‌ها را ارسال کنید، متد POST ترجیح دارد، زیرا، مرورگرها محدودیت اندازه URL را دارند و اغلب سرورها نیز برای پذیرش URL محدودیت تعیین کرده‌اند.

بازیابی داده‌ها در سمت سرور

هر نوع متد HTTP که انتخاب کنید، سرور رشته‌ای متنی دریافت خواهد کرد که آن را تجزیه می‌کند تا داده‌هایش را به صورت لیستی از جفت‌های کلید/مقدار به دست آورد. روش دسترسی به این لیست به پلتفرم توسعه یا فریمورک خاصی که مورد استفاده قرار می‌دهید مرتبط خواهد بود. آن فناوری که استفاده می‌کنید روی روش مدیریت کلیدهای تکراری نیز تأثیر دارد. در اغلب موارد مقداری که اخیراً برای یک کلید، فعال بوده است ترجیح دارد.

مثال: PHP خام

PHP برخی اشیای سراسری برای دسترسی به داده‌ها ارائه می‌کند. با فرض این که از متد POST استفاده می‌کنید، مثال زیر صرفاً داده‌ها را می‌گیرد و آن‌ها را به کاربر نمایش می‌دهد. البته این که با داده‌ها چه می‌کنید به شما مربوط است. می‌توان داده‌ها را نمایش داد، آن‌ها را در پایگاه داده ذخیره کرد، به وسیله ایمیل ارسال نمود یا به روش دیگری مورد پردازش قرار داد.

مثال زیر صفحه‌ای را به همراه داده‌های ارسالی نمایش می‌دهد. در فایل زیر روش استفاده عملی از آن را مشاهده می‌کنید:

فایل فوق شامل همان مثالی است که قبلاً دیدیم و دارای متد POST و یک action به صورت php-example.php است. زمانی که این فرم تحویل می‌شود، داده‌ها را به فایل php-example.php ارسال می‌کند که شامل کد PHP است که در قطعه کد فوق مشاهده می‌کنید. زمانی که این کد اجرا شود، خروجی مرورگر به صورت Hi Mom خواهد بود.

ارسال داده

نکته: مثال فوق زمانی که صفحه را به صورت لوکال در مرورگر بارگذاری کنید کار نمی‌کند، زیرا مرورگرها نمی‌توانند کد PHP را تفسیر کنند، بنابراین وقتی که فرم تحویل می‌شود، مرورگر صرفاً پیشنهاد می‌کند که فایل PHP را دانلود کنید. برای این که این مثال عملیاتی شود، باید آن را روی نوعی سرور PHP اجرا کنید. گزینه‌های مناسب برای تست PHP نرم‌افزارهای MAMP و AMPPS هستند.

مثال: پایتون

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

دو قالبی که در کد فوق مورد ارجاع قرار گرفته‌اند به شرح زیر هستند:

  • form.html – این همان فرمی است که قبلاً در بخش متد POST دیدیم، اما action آن روی {{ (‘url_for(‘hello }} تنظیم شده است. در واقع این یک قالب Jinja2 است که اساساً فایل HTML است اما می‌تواند شامل فراخوانی‌هایی به کد پایتون باشد که وب‌سرور درون آکولادها را اجرا می‌کند. (‘url_for(‘hello اعلام می‌کند که وقتی فرم تحویل شد، باید به آدرس hello/ ریدایرکت شود.
  • greeting.html – این قالب صرفاً شامل یک خط است که دو بیت از داده‌های ارسالی به آن را در زمان رندر شدن پردازش می‌کند. این کار از طریق تابع ()hello انجام می‌یابد که در بالا دیدیم و زمانی اجرا می‌شود که به URL با نام hello/ مراجعه کنید.

نکته: این کد نیز در صورتی که آن را مستقیماً در مرورگر بارگذاری کنید کار نخواهد کرد. طرز کار پایتون اندکی با PHP متفاوت است. برای این که بتوانید آن را به صورت لوکال اجرا کنید باید Python/PIP را نصب کنید. سپس باید Flask را با استفاده از دستور زیر نصب کنید:

pip3 install flask

در ادامه باید مثال را با استفاده از دستور زیر اجرا کنید:

python3 python-example.py

سپس به آدرس localhost:5000 در مرورگر خود بروید.

زبان‌ها و فریمورک‌های دیگر

فناوری‌های سمت سرور زیاد دیگری نیز وجود دارند که می‌توانید برای مدیریت فرم استفاده کنید و شامل Perl ،Java ،.Net ،Ruby و غیره هستند. شما می‌توانید هر کدام را که دوست دارید انتخاب کنید. البته لازم به ذکر است که استفاده مستقیم از این فناوری‌ها بسیار نامعمول است، زیرا کار با آن‌ها کمی دشوار است. به طور معمول افراد از فریمورک‌های خوبی مانند لیست زیر که وجود دارند استفاده می‌کنند تا مدیریت فرم‌ها را به روش آسان‌تر انجام دهند:

  • Django برای پایتون (کمی سنگین‌تر از Flask است، اما ابزارها و گزینه‌های بیشتری دارد.)
  • Express برای Node.js
  • Laravel برای PHP
  • Ruby On Rails برای Ruby
  • Phoenix برای Elixir

لازم به ذکر است که حتی در صورت استفاده از این فریمورک‌ها نیز، کار با فرم‌ها لزوماً آسان نیست. اما به هر حال نسبت به تلاش برای نوشتن همه کارکردها از صفر بسیار آسان‌تر است و صرفه‌جویی زمانی زیادی ایجاد می‌کند.

نکته: آموزش همه زبان‌ها یا فریمورک‌های سمت سرور خارج از حیطه این مقاله است.

ارسال فایل به عنوان یک حالت خاص

ارسال فایل‌ها با استفاده از فرم‌های HTML یک حالت خاص است. فایل‌ها داده‌های باینری هستند یا دست کم این گونه تصور می‌شوند، در حالی که همه داده‌های دیگر متنی هستند. از آنجا که HTTP یک پروتکل متنی است، الزامات خاصی برای مدیریت داده‌های باینری وجود دارند.

خصوصیت encrypt

این خصوصیت امکان تعیین مقدار هدر HTTP به صورت Content-Type را می‌دهد که در درخواست ایجاد شده هنگام تحویل فرم جای می‌گیرد. این هدر بسیار مهم است، زیرا به سرور اعلام می‌کند که چه نوع داده‌هایی ارسال می‌شوند. مقدار آن به صورت پیش‌فرض به صورت زیر است:

application/x-www-form-urlencoded

به زبان ساده معنای آن چنین است که: «این یک داده فرم است که به صورت پارامترهای URL کدگذاری شده است.»

اگر بخواهید فایل‌ها را ارسال کنید، باید گام‌های بیشتری بردارید:

  • خصوصیت method را به صورت POST تعیین کنید، زیرا محتوای فایل نمی‌تواند درون پارامترهای URL قرار گیرد.
  • مقدار enctype را برای multipart/form-data تعیین کنید، زیرا داده‌ها به بخش‌های چندگانه تقسیم می‌شوند، که یکی برای هر فایل و دیگری برای داده‌های متنی درون بدنه فرم است. این بخش دوم زمانی است که متن نیز در فرم وارد شده باشد.
  • یک یا چند ویجت File picker نیز در فرم قرار می‌گیرد تا کاربر بتواند فایل (هایی) که می‌خواهد آپلود کند را انتخاب نماید.

برای نمونه:

نکته: برخی مرورگرها از خصوصیت multiple روی عنصر <input> پشتیبانی می‌کنند و بدین ترتیب می‌توانید بیش از یک فایل را به وسیله تنها یک عنصر <input> برای آپلود انتخاب کنید. این که سرور چگونه این فایل‌ها را در عمل مدیریت می‌کند، به فناوری مورد استفاده در سمت سرور بستگی دارد. چنان که پیش‌تر گفتیم، استفاده از یک فریمورک موجب می‌شود که کارها بسیار آسان‌تر شوند.

هشدار: بسیاری از سرورها با محدودیت اندازه برای فایل‌ها و درخواست‌های HTTP پیکربندی شده‌اند تا از سوءاستفاده‌های احتمالی جلوگیری کنند. پیش از ارسال یک فایل، این محدودیت را با مدیر سرور بررسی کنید.

دغدغه‌های رایج امنیتی

هر بار که داده‌ها را به سرور ارسال می‌کنید، باید بحث امنیت را در نظر داشته باشید. فرم‌های HTML با اختلاف زیاد یکی از مهم‌ترین هدف‌های حملات رایج به سرورها هستند. البته این مشکلات هرگز از خود فرم‌های HTML ناشی نمی‌شوند، بلکه ناشی از شیوه مدیریت داده‌ها از سوی سرور هستند.

بسته به این که می‌خواهد چه کار بکنید، برخی مشکلات کاملاً شناخته‌شده امنیتی وجود دارند که ممکن است با آن‌ها مواجه شوید:

XSS و CSRF

اسکریپت‌نویسی بین سایت (XSS) و جعل درخواست بین سایت (CSRF) دو نوع رایج از حمله‌هایی هستند که در زمان نمایش داده‌های ارسالی یک کاربر به کاربر دیگر رخ می‌دهند.

حمله XSS به مهاجم امکان می‌دهد که اسکریپت‌های سمت کلاینت را در صفحه‌های وب مشاهده‌شده از سوی کاربران دیگر تزریق کند. آسیب‌پذیری اسکریپت‌نویسی بین سایت از سوی مهاجمان برای دور زدن کنترل‌هایی مانند «سیاست ریشه یکسان» (same origin policy) را فراهم می‌سازد. تأثیر این حمله‌ها متفاوت است و از یک مزاحمت ساده تا ریسک‌های امنیتی مهم متغیر خواهد بود.

حمله‌های CSRF مشابه حمله‌های XSS هستند، چون به روش مشابهی آغاز می‌شوند و اسکریپت سمت کلاینت در صفحه‌های وب تزریق می‌شود، اما هدف آن‌ها متفاوت است. مهاجمان CSRF تلاش می‌کنند تا دسترسی‌های مربوط به کاربران با دسترسی بالاتر (مانند مدیر سایت) را به دست آورند تا اقداماتی را که نباید انجام دهند به اجرا درآورند. برای مثال بدین ترتیب می‌توانند داده‌ها را به یک کاربر غیر معتمد ارسال کنند.

حمله‌های XSS از اعتمادی که یک کاربر به وب‌سایت دارد سوءاستفاده می‌کنند، در حالی که حمله‌های CSRF از اعتمادی که وب‌سایت به کاربران خود دارد سوءاستفاده می‌کنند.

برای جلوگیری از این حمله‌ها، باید همواره داده‌هایی که یک کاربر به سرور ارسال می‌کند (در صورت نیاز به نمایش دادن) را بررسی کنید و تلاش کنید محتوای HTML ارائه‌شده از سوی کاربر را نمایش ندهید. به جای آن باید داده‌های ارائه‌شده از سوی کاربر را پردازش کنید تا به صورت خام نمایش پیدا نکند. تقریباً همه فریمورک‌های موجود امروزه یک فیلتر کمینه پیاده‌سازی کرده‌اند که عناصر <script> ،<iframe> و <object> مربوط به HTML را از داده‌های ارسالی از سوی هر کاربر حذف می‌کنند. بدین ترتیب ریسک کاهش پیدا می‌کند، اما لزوماً رفع نمی‌شود.

تزریق SQL

تزریق SQL نوعی از حمله است که در آن مهاجم تلاش می‌کند اقدامی را روی یک پایگاه داده مورد استفاده از سوی وب‌سایت هدف اجرا نماید. این نوع حمله عموماً شامل ارسال یک درخواست SQL به امید اجرا شدن آن از سوی سرور است. این اتفاق معمولاً زمانی رخ می‌دهد که سرور اپلیکیشن تلاش می‌کند داده‌های ارسالی از سوی کاربر را در پایگاه داده ذخیره کند. در واقع این نوع حمله یکی از اصلی‌ترین نوع حملات در برابر وب‌سایت‌ها محسوب می‌شود.

عواقب چنین حملاتی می‌تواند فاجعه‌آمیز باشد و از فقدان داده‌ها تا حملاتی برای به دست آوردن کنترل زیرساخت وب‌سایت از طریق کسب دسترسی‌های مورد نیاز متفاوت است. این یک تهدید بسیار جدی است و شما هرگز نباید داده‌های ارسالی از سوی کاربر را بدون اجرای نوعی مراحل پاکسازی در پایگاه داده ذخیره کنید.

تزریق هدر HTTP و تزریق ایمیل

این نوع از حمله‌ها زمانی رخ می‌دهند که اپلیکیشن هدرهای HTTP یا ایمیل‌ها را بر مبنای داده‌های وارد شده از سوی کاربر در یک فرم می‌سازد. این حمله به صوت مستقیم به سرور شما آسیب نمی‌زند و کاربران را نیز متأثر نمی‌سازد، اما یک درِ باز برای مشکلات عمیق‌تری مانند سرقت «نشست» (Session) یا حمله‌های فیشینگ فراهم می‌کند.

این نوع حمله‌ها عموماً خاموش هستند و می‌توانند سرور شما را به یک زامبی تبدیل کنند.

همیشه مشکوک باشید و به کاربرانتان اعتماد نکنید

اینک سؤال این است که چگونه می‌توان با این تهدیدها مبارزه کرد؟ پاسخ دادن به این سؤال نیاز به یک راهنمای کاملاً مفصلی دارد، اما به طور خلاصه چند قاعده وجود دارد که باید در ذهن خود داشته باشید. مهم‌ترین قاعده این است که هرگز به کاربرانتان که شامل خود شما نیز می‌شود اعتماد نکنید. حتی یک کاربر معتمد هم می‌تواند به سرقت رفته باشد.

همه داده‌هایی که به سرور می‌آیند باید بررسی و پاکسازی شوند. هیچ استثنایی در این خصوص وجود ندارد.

  • کاراکترهای بالقوه خطرناک را escape کنید. در مورد کاراکترهای خاص باید با احتیاط رفتار کنید، زیرا بستگی زیادی به زمینه کاربرد داده‌ها و پلتفرم سروری که استفاده می‌کنید دارد، اما همه زبان‌های سمت سرور، تابع‌هایی به این منظور دارند.
  • مقدار داده‌های مجاز ورودی را صرفاً محدود به مقدار مورد نیاز بکنید.
  • فایل‌های آپلود شده را در محیط ایزوله (Sandbox) نگهداری کنید. به این منظور باید فایل‌ها را روی یک سرور مجزا نگه‌داری کنید و تنها از طریق زیردامنه متفاوت و یا بهتر از آن از طریق نام دامنه کاملاً متفاوت امکان دسترسی به آن را فراهم سازید.

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

سخن پایانی

چنان که مشاهده کردید، ارسال داده‌های فرم کار آسانی است، اما امن سازی یک اپلیکیشن چنین نیست. کافی است به خاطر بسپارید که یک توسعه‌دهنده فرانت‌اند تنها کسی نیست که مدل امنیتی داده‌ها را تعریف می‌کند. چنان که در این مقاله دیدیم امکان اجرای اعتبارسنجی داده‌ها در سمت کلاینت وجود دارد، اما سرور نمی‌تواند به این اعتبارسنجی اعتماد کند، زیرا هیچ راهی برای دانستن این که در سمت کلاینت چه اتفاقی رخ داده است ندارد.

منبع: فرادرس 

راهنمای سریع Regex — فهرست کاربردی

«عبارت‌های منظم» (Regular Expressions) که اصطلاحاً regex یا regexp نامیده می‌شوند در زمان استخراج اطلاعات از هر متنی کاملاً مفید هستند. این عبارت‌ها برای جستجو و یافتن مطابقت یک یا چند الگوی جستجوی خاص مورد استفاده قرار می‌گیرند. بدین ترتیب می‌توان توالی خالصی از کاراکترهای ASCII یا یونیکد را یافت. زمینه‌های کاربرد regex از اعتبارسنجی تا تجزیه/جایگزینی رشته‌ها، ترجمه داده‌ها به قالب‌های دیگر و وب اسکرپینگ متفاوت است.

یکی از جالب‌ترین قابلیت‌های Regex این است که پس از یادگیری ساختار آن می‌توانید در تقریباً همه زبان‌های برنامه‌نویسی شامل JavaScript ،Java ،VB ،C# ،C / C++ ،Python ،Perl ،Ruby ،Delphi ،R ،Tcl و بسیاری دیگر استفاده کنید. تنها تفاوت در این است که برخی زبان‌ها از برخی قابلیت‌های پیشرفته‌تر و نسخه‌های ساختار متفاوت پشتیبانی می‌کنند.

در ادامه برخی نمونه‌ها را مورد بررسی قرار داده و در مورد هر کدام توضیح می‌دهیم.

موضوعات ابتدایی Regex

در این بخش برخی موضوعات کاملا مقدماتی مرتبط با regex را مورد بررسی قرار می‌دهیم.

مهارها – ^ و $

  • The^ – با هر رشته‌ای که با The آغاز شود تطبیق می‌یابد.
  • $end – با هر رشته‌ای که به کلمه end خاتمه یابد تطبیق می‌یابد.
  • $The end^ – تطبیق دقیق رشته‌ای را تعریف می‌کند، یعنی رشته مورد جستجو باید با The آغاز و با end خاتمه یابد.
  • roar  – با هر رشته‌ای که کلمه roar در آن باشد تطبیق می‌یابد.

سورها – * + ? و {}

  • *abc – با هر رشته‌ای که در ابتدایش کاراکترهای ab و در ادامه صفر یا چند c داشته باشد تطبیق می‌یابد.
  • +abc – با هر رشته‌ای که در ابتدایش کاراکترهای ab و در ادامه یک یا چند کاراکتر c داشته باشد تطبیق می‌یابد.
  • ?abc – با هر رشته‌ای که در ابتدایش کاراکترهای ab و در ادامه صفر یا یک کاراکتر c داشته باشد تطبیق می‌یابد.
  • {abc{2 – با هر رشته‌ای که در ابتدایش کاراکترهای ab و در ادامه دقیقاً 2 کاراکتر c داشته باشد تطبیق می‌یابد.
  • {,abc{2 – با هر رشته‌ای که در ابتدایش کاراکترهای ab و در ادامه 2 یا بیشتر کاراکتر c داشته باشد تطبیق می‌یابد.
  • {abc{2,5 – با هر رشته‌ای که در ابتدایش کاراکترهای ab و در ادامه 2 تا 5 کاراکتر c داشته باشد تطبیق می‌یابد.
  • *(a(bc – با هر رشته‌ای که در ابتدایش کاراکتر a و در ادامه صفر یا چند کپی از دنباله bc داشته باشد تطبیق می‌یابد.
  • {a(bc){2,5 – با هر رشته‌ای که در ابتدایش کاراکتر a و در ادامه 2 تا 5 کپی از دنباله bc داشته باشد تطبیق می‌یابد.

عملگر OR – | یا []

  • (a(b|c – با هر رشته‌ای که یک کاراکتر a و در ادامه b یا c داشته باشد تطبیق می‌یابد.
  • [a[bc – همانند regex قبلی است.

دسته‌های کاراکتر – d \w \s\ و .

  • d\ – با یک کاراکتر منفرد که رقم باشد تطبیق می‌یابد.
  • w\ – با یک کاراکتر کلمه (کاراکتر حرفی/عددی به علاوه زیرخط) تطبیق می‌یابد.
  • s\ – با کاراکتر خالی تطبیق می‌یابد (شامل tab و line break نیز می‌شود).
  • . – با هر کاراکتری تطبیق می‌یابد.

باید از عملگر  (.) با احتیاط استفاده کنید، چون در اغلب موارد کلاس یا کلاس کاراکتر منفی آن (که در ادامه معرفی می‌کنیم) سریع‌تر و بسیار دقیق‌تر است. حالت نفی d ،\w\ و s\ به ترتیب D ،\W\ و S\ هستند. برای نمونه D\ تطبیق معکوس را با توجه به آن چیزی که با d\ به دست می‌آید ارائه می‌کند.

  • D\ – با یک کاراکتر غیر رقمی منفرد تطبیق می‌یابد.

برای این که به صورت عملی از آن استفاده کنید، باید کاراکترها را با استفاده از \ به صورت escape درآورید چون معنای خاصی دارند.

  • d\$\ – با رشته‌ای تطبیق می‌یابد که یک $ پیش از یک رقم دارد.

توجه کنید که می‌توانید کاراکترهای غیر قابل چاپ مانند t\، خطوط جدید n\، بازگشت‌های carriage یعنی r\ را نیز تطبیق دهید.

فلگ‌ها

تا به این جا در مورد شیوه ساخت یک regex مطالبی آموختیم، اما یک مفهوم بنیادی به نام فلگ را فراموش کرده‌ایم.

Regex معمولاً به صورت /abc/ ارائه می‌شود که الگوی جستجو به وسیله دو کاراکتر اسلش متمایز شده است. در انتهای عبارت منظم می‌توان یک فلگ با مقادیر زیر تعیین کرد (امکان ترکیب کردن آن‌ها با هم نیز وجود دارد):

  • g (سراسری یا global) – پس از نخستین تطبیق بازگشت نمی‌یابد و جستجوهای بعدی را از انتهای مورد مطابقت قبلی آغاز می‌کند.
  • m (چندخطی یا multi-line) – زمانی که ^ و $ به جای کل رشته با ابتدا و انتهای یک خط مطابقت داشته باشند.
  • i (غیر حساس یا insensitive) – موجب می‌شود که کل عبارت منظم از حالت حساس به حروف کوچک یا بزرگ خارج شود. برای نمونه aBc/i/ می‌تواند با AbC تطبیق یابد.

موضوعات سطح متوسط

در این بخش برخی مباحث regex را بررسی می‌کنیم که فراتر از سطح مقدماتی قبلی هستند، اما هنوز وارد حیطه پیشرفته نشده‌ایم.

گروه‌بندی و capture – ()

  • (a(bc – پرانتزها یک گروه تشکیل می‌دهند که مقدار bc را به دست می‌دهد.
  • *(a(?:bc – ما با استفاده از : ? گروه capturing را غیر فعال می‌کنیم.
  • (a(?<foo>bc – با استفاده از <foo>? یک نام برای گروه خود تعیین می‌کنیم.

این عملگر در مواردی که لازم است اطلاعات از رشته‌ها یا داده‌ها با استفاده از زبان برنامه‌نویسی خاصی استخراج شود کاملاً مفید خواهند بود. هر رخداد چندگانه به وسیله چند گروه به دست می‌آید و در یک آرایه کلاسیک عرضه می‌شود، یعنی می‌توان با استفاده از یک اندیس روی نتیجه تطبیق به هر رخداد مجزا دسترسی داشت.

اگر بخواهیم برای گروه‌های خود نام‌گذاری کنیم، می‌توانیم مقادیر گروه را با استفاده از نتیجه تطبیق یافته مانند یک دیکشنری به دست آوریم که در آن کلیدها نام هر گروه خواهند بود.

عبارت‌های براکت – []

  • [abc] – با هر رشته‌ای که یک a یا ab یا a c داشته باشد، تطبیق می‌یابد و معادل a|b|c است.
  • [a-c]  همانند قبلی است.
  • [a-fA-F0-9] – با رشته‌ای تطبیق می‌یابد که نماینده یک رقم هگزادسیمال منفرد است و حساسیت به حروف کوچک یا بزرگ ندارد.
  • [0-9]%  – با رشته‌ای تطبیق می‌یابد که پیش از علامت % یک کاراکتر از 0 تا 9 دارد.
  • [^a-zA-Z] – با رشته‌ای تطبیق می‌یابد که حرفی از a تا z یا از A تا Z ندارد. در این حالت ^ به عنوان منفی عبارت استفاده می‌شود.

به خاطر داشته باشید که درون عبارت‌های براکتی، همه کاراکترهای خاص (شامل بک اسلش \) قدرت خاص خود را از دست می‌دهند. بدین ترتیب قاعده scape قابل استفاده نیست.

تطبیق Greedy و Lazy

سورها یعنی * + {} عملگرهای حریص (Greedy) هستند، بنابراین مورد تطبیق را تا آنجا که ممکن است روی متن مورد نظر گسترش می‌دهند.

برای نمونه <+.> با <div>simple div</div> در عبارت This is a <div> simple div</div> test تطبیق می‌یابد. برای این که تنها تگ div تطبیق یابد، می‌توانیم از یک ? استفاده کنیم تا آن را تنبل (Lazy) کنیم:

  • <?+.> – با هر کاراکتری که یک یا چند بار درون < و > آمده باشد تطبیق می‌یابد و در صورت نیاز گسترش می‌یابد.

توجه داشته باشید که یک راه‌حل بهتر عدم استفاده از . به نفع یک regex صریح‌تر است:

  • <+[<>^]> –  با هر کاراکتری به جز > یا < که یک یا چند بار درون < و > قرار داشته باشد تطبیق می‌یابد.

موضوعات پیشرفته

در این بخش، برخی مباحث پیشرفته مرتبط با regex را مورد بررسی قرار می‌دهیم.

کران‌ها  b\ و B\

babc\b\ – یک جستجوی «صرفاً کل کلمه» اجرا می‌کند.

b\ نشان‌دهنده یک مهار مانند caret است (مشابه $ و ^ است) و با موقعیت‌هایی تطبیق می‌یابد که یک طرف یک کاراکتر کلمه (مانند w\) است و طرف دیگر کاراکتر کلمه نیست، مثلاً می‌تواند ابتدای یک رشته یا یک کاراکتر فاصله باشد.

حالت نفی این کران به صورت B\ است. این کران با همه موقعیت‌های تطبیق می‌یابد که b\ تطبیق پیدا نکند و می‌توان از آن برای یافتن الگوی جستجویی استفاده کرد به طور کامل در کاراکترهای کلمه‌ای احاطه شده است.

Babc\B\ تنها در صورتی تطبیق می‌یابد که الگو به صورت کامل در کاراکترهای کلمه احاطه شده باشد.

ارجاع به عقب   1\

([abc])\1 

استفاده از 1\ موجب می‌شود که نتیجه با همان متنی تطبیق پیدا کند که در گروه نخست capture مطابقت یافته است.

([abc])([de])\2\1 

می‌توان از 2\ (3\، 4\ و غیره) نیز برای شناسایی همان متنی که در گروه capture مورد دوم (سوم و چهارم و غیره) تطبیق می‌یابد استفاده کرد.

(?<foo>[abc])\k<foo> 

می‌توان نام foo را روی گروهی قرار داد و آن را بعداً مورد اشاره قرار داد. نتیجه همان regex نخست است.

گشتن در سمت جلو و عقب (=?) و (=>?)

d(?=r)

با یک d تنها در صورتی تطبیق می‌یابد که در ادامه r آمده باشد، اما r بخشی از تطبیق regex کلی نخواهد بود.

(?<=r)d

تنها در صورتی با یک d تطبیق پیدا می‌کند که قبل از آن یک r آمده باشد، اما r جزئی از تطبیق regex کلی نخواهد بود.

همچنین می‌توان از عملگر نفی نیز استفاده کرد:

d(?!r)

تنها در صورتی با یک d تطبیق می‌یابد که در ادامه r نیامده باشد، اما r بخشی از تطبیق regex کلی نخواهد بود.

(?<!r)d

تنها در صورتی با یک d تطبیق پیدا می‌کند که قبل از آن یک r نیامده باشد، اما r جزئی از تطبیق regex کلی نخواهد بود.

جمع‌بندی

چنان که دیدیم کاربردهای اپلیکیشن regex می‌توانند کاملاً متنوع باشند. مطمئناً شما دست کم یکی از این وظایف را تاکنون در زمان اجرای وظایف توسعه نرم‌افزار خود مشاهده کرده‌اید. در ادامه این کاربردها را در یک فهرست خلاصه جمع‌بندی کرده‌ایم.

  • اعتبارسنجی داده – برای نمونه آیا یک رشته زمانی به درستی ترکیب یافته است یا نه.
  • کاوش داده – به خصوص وب اسکرپینگ که در آن همه صفحه‌هایی که شامل مجموعه خاص از کلمات هستند در نهایت با ترتیب خاصی پیدا می‌شوند.
  • دستکاری داده‌ها – تبدیل داده از قالب خام به یک قالب دیگر.
  • تجزیه متن – برای نمونه گردآوری همه پارامترهای GET در URL و دریافت یک متن که درون یک مجموعه از پرانتزها قرار دارد.
  • جایگزینی رشته – برای نمونه در زمان کدنویسی در یک IDE برای ترجمه کلاس جاوا به سی شارپ در شیء JSON متناظر باید (;) با (,) جایگزین شوند، به حالت حروف کوچک درآید و hc اعلان نوع خودداری شود.
  • هایلایت کردن ساختار، تغییر دادن نام فایل‌ها، بررسی بسته‌ها و بسیاری کاربردهای دیگر که با رشته‌ها سرور کار دارند و داده‌ها باید متنی باشد، همگی با استفاده از regex قابل اجرا هستند.

منبع" فرادرس

مفیدترین ترفندهای پایتون — فهرست کاربردی

در این مقاله به بررسی ترفندهای مفید و مهم زبان برنامه‌نویسی پایتون می‌پردازیم. شما با بهره‌گیری از این ترفندهای پایتون می‌توانید در زمان و تلاش خود صرفه‌جویی زیادی بکنید.

خارج کردن آیتم‌های آرایه از حالت فشرده

با استفاده از روش معرفی‌شده در مثال زیر می‌توانید آیتم‌های آرایه را برحسب نامشان مورد دسترس قرار دهید:

تعویض متغیرها

در مثال زیر با روشی آشنا می‌شوید که امکان تعویض متغیرها را فراهم می‌سازد:

پروفایل و آمار کد

با استفاده از کتابخانه‌های معرفی‌شده در کد زیر می‌توانید به برخی آمار مرتبط با کد پایتون خود دسترسی پیدا کنید:

تکرار رشته

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

برش دادن

با استفاده از روش ساده معرفی‌شده در کد زیر می‌توانید یک رشته را به هر نوع و طولی که دوست دارید برش دهید:

معکوس‌سازی

در کد زیر مثالی از روش معکوس سازی یک رشته را مشاهده می‌کنید:

اندیس منفی

اگر می‌خواهید از آخرین کاراکتر یک رشته آغاز کنید، می‌توانید از اندیس منفی استفاده کنید:

مجموعه‌های متقاطع

برای این که بتوانید آیتم‌های مشترک دو مجموعه را به دست آورید، می‌توانید از کد زیر استفاده کنید:

تفاوت مجموعه‌ها

برای به دست آوردن اختلاف بین مجموعه‌ها، یعنی آیتم‌هایی که در اشتراک دو مجموعه نیستند به روش زیر عمل می‌کنیم:

اجتماع کالکشن‌ها

برای به دست آوردن ترکیب صریح دو مجموعه از کد زیر استفاده می‌کنیم:

آرگومان‌های اختیاری

با ارائه یک مقدار پیش‌فرض برای یک آرگومان می‌توان آرگومان‌های اختیاری ارسال کرد:

آرگومان‌های ناشناس با استفاده از arguments*

اگر تابع شما می‌تواند هر تعداد آرگومان دریافت کند در این صورت می‌توانید یک کاراکتر ستاره (*) در ابتدای نام پارامتر قرار دهید:

دیکشنری به عنوان آرگومان با استفاده از arguments**

با بهره‌گیری از arguments** می‌توان تعداد متغیری از آرگومان‌های کلیدواژه را به یک تابع ارسال کرد. بدین ترتیب می‌توان مقادیر دیکشنری را به عنوان آرگومان‌های کلیدواژه ارسال کرد:

تابع با خروجی‌های چندگانه

اگر تابعی الزام به بازگشت مقادیر چندگانه داشته باشد، در این صورت:

ترفندهای پایتون

حلقه‌های تک‌خطی

با استفاده از روش زیر می‌توانید حلقه‌های تک‌خطی بنویسید:

ترکیب کردن لیست‌ها با استفاده از Zip

در مثال زیر روشی مشاهده می‌کنید که:

  • چند کالکشن می‌گیرد و یک کالکشن جدید بازمی‌گرداند.
  • کالکشن جدید شامل آیتم‌هایی است که در آن هر آیتم شامل عناصری از هر کالکشن ورودی است.
  • این روش امکان پیمایش چند کالکشن را به طور همزمان فراهم می‌سازد:

آزادسازی حافظه

فرایند garbage collection دستی را می‌توان به صورت زمان‌بندی‌شده یا با سازوکاری مبتنی بر رویداد اجرا کرد:

استفاده از دکوراتورها

  • دکوراتورها می‌توانند بر کارکرد کد بیفزایند. آن‌ها اساساً تابع‌هایی هستند که شیءها / تابع‌های دیگر را فراخوانی می‌کنند. آن‌ها تابع‌های قابل فراخوانی هستند و از این رو شیئی بازگشت می‌دهند که می‌توان در ادامه هنگامی که تابع دکوراتور اجرا می‌شود، فراخوانی شود.
  • دکوراتورها امکان برنامه‌نویسی «جنبه گرا» (aspect-oriented) را فراهم می‌سازند.
  • ما می‌توانیم یک کلاس/تابع را پوشش دهیم و سپس هر زمان که تابعی فراخوانی شود، یک کد خاص اجرا می‌‌شود.

مثال زیر شیوه نمایش نام تابع را نمایش می‌دهد. این تنها یک کد مثالی است که نشان می‌دهد چگونه می‌توانیم یک دکوراتور را فراخوانی کنیم. می‌توان از دکوراتورها برای فراخوانی لاگرها، اجرای عملیات امنیتی و موارد دیگر استفاده کرد.

اکنون از آن در تابع خود استفاده می‌کنیم:

Unzip کردن

در کد زیر با روش Unzip کردن آشنا می‌شویم:

الحاق کالکشن

در این بخش با کد مرتبط با روش الحاق دو کالکشن مختلف با هم آشنا می‌شویم:

دسترسی به حافظه یک شیء

در برخی موارد لازم است که مستقیماً به آدرس حافظه یک متغیر دسترسی داشته باشیم:

نمایش دایرکتوری جاری

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

نمایش ماژول‌های ایمپورت شده

برای دیدن این که کدام ماژول‌ها در برنامه ایمپورت شده‌اند می‌توانید از کد زیر استفاده کنید:

دریافت شناسه پردازش جاری

برای این که شناسه پردازش جاری را به دست آورید می‌توانید از کد زیر استفاده کنید:

بدین ترتیب به پایان این مقاله می‌رسیم. امیدواریم از مطالعه این نکات و ترفندهای زبان برنامه‌نویسی پایتون لذت برده باشید.

منبع: فرادرس