در این راهنما نگاهی به الگوریتم مرتب سازی ادغامی و پیادهسازی آن در جاوا خواهیم داشت. مرتبسازی ادغامی یکی از مؤثرترین تکنیکهای مرتبسازی بر مبنای پارادایم «تقسیم و حل» (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 است، چون آرایههای موقتی در هر فراخوانی بازگشتی ایجاد میشوند.
در این راهنمای کوتاه، با طرز کار الگوریتم مرتبسازی ادغامی و روش پیادهسازی آن در جاوا آشنا شدیم. همه کدهای این راهنما را میتوانید در این صفحه گیتهاب (+) ملاحظه کنید.
منبع: فرادرس
پلتفرم اندروید بازه وسیعی از ویجتهای رابط کاربری را ارائه کرده است که برای نیازهای اغلب اپلیکیشنها کافی هستند. این ویجتها عالی هستند و محصولات نهایی کاملاً کاربردی و زیبایی را میسازند؛ اما در پارهای موارد توسعهدهندگان نرمافزار تمایل دارند فراتر از این بیندیشند و رابطهای سفارشی خاص خود را بسازند. بهترین روش برای پاسخ دادن به این خلاقیت، ساخت نمای سفارشی است.
در این نوشته به تعریف و برسی نماهای سفارشی و سپس بخش جذابتر یعنی نمایش آنها خواهیم پرداخت.
در آغاز باید برخی اصطلاحهای مقدماتی را برای درک بهتر تعریف کنیم.
نمای اندروید یا Android View یک کلاس مبنا برای ساخت رابط کاربری است که به توسعهدهندگان فرصتی برای ایجاد طراحیهای پیچیده میدهد. این نما ناحیهای مستطیلی روی صفحه اشغال میکند و مسئول اندازهگیری، طرحبندی و ترسیم خود در راستای عناصر فرزندش است. به علاوه نماها همه ورودیهای کاربر را مدیریت میکنند.

یک گروه نما یا ViewGroup، نمای خاصی است که توانایی گنجاندن نماهای دیگر (فرزندان) را در خود دارد و مشخصات طرحبندی خاص خود را تعریف میکند. این نما همچنین محلی است که هر نمای فرعی (subview) میتواند از آن مشتق شود.
هر نمایی که خارج از ویجت مبنای اندروید ایجاد شود را میتوان یک نمای سفارشی یا Custom View دانست. در این نوشته کل توجه ما روی این نمای سفارشی است.
روشهای بسیار مختلفی برای پیادهسازی نماهای سفارشی وجود دارند و رویکردی که انتخاب میشود به نیازهای شما بستگی دارد. در ادامه برخی روشها را مورد بررسی قرار میدهیم:
این روش زمانی که حجم بالایی از کد راهاندازی برای نمای شما لازم باشد و بخواهید از آن در جاهای مختلفی استفاده کنید مناسب خواهد بود. برای اجتناب از درج کد شلوغ درون اکتیویتی یا فرگمان، میتوانید ویجت مبنا را بسط داده و همه کارهای راهاندازی را درون سازنده انجام دهید، از این رو میتوان به سادگی از آن مجدداً استفاده کرد. این روش به طور بدیهی سادهترین رویکرد برای پیادهسازی نماهای سفارشی محسوب میشود.
اگر میخواهید ابتکار به خرج بدهید و همه کارها را از صفر خودتان اجرا کنید، این روش مناسب خواهد بود. در این روش شما همه منطق رفتاری ویجت را خودتان رسم، اندازهگیری و برنامهریزی میکنید.
در پارهای اوقات مجموعهای از ویجتها دارید که میخواهید آنها را با هم گروهبندی کنید تا یک نمای کاملاً جدید ایجاد کنید. برای نمونه فرض کنید یک Textview و یک Button دارید و میخواهید آنها را درون یک LinearLayout گروهبندی کنید. این نما عموماً به نام «نمای ترکیبی» (Compound View) شناخته میشود. مزیتهای این کار به شرح زیر هستند:
در این بخش به توضیح روش اندروید برای رسم نماها میپردازیم. در آغاز سه مرحله وجود دارند که پیش از نمایش نهایی نما روی صفحه باید اجرا شوند. این سه مرحله، اندازهگیری، طرحبندی و رسم هستند. هر یک از مراحل به صورت پیمایش «عمق-اول» (depth-first) سلسلهمراتب نما است که از والد به سمت فرزند حرکت میکند. در هر مرحله متدی وجود دارند که میتواند بسته به نیازها override شده و تغییر یابد. این فرایند را میتوان به دو مرحله تقسیم کرد:

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

نمای فرزند اقدام به تعریف LayoutParams به صورت برنامهنویسی شده و یا در XML میکند و والد این مقادیر را با استفاده از متد ()getLayoutParams بازیابی میکند.
والد، MeasureSpecs را محاسبه کرده و آن را با استفاده از ()child.measure در سلسلهمراتب به سمت پایین ارسال میکند. Measurespecs شامل حالت و مقدار است.
سه حالت برای اندازهگیری وجود دارند:
متد ()onMeasure به همراه پارامترهای MeasureSpecs فراخوانی میشود. در این متد View اقدام به محاسبه عرض/ارتفاع مطلوب میکند و آن را با استفاده از setMeasuredDimension تنظیم میکند. به خاطر داشته باشید که متد setMeasuredDimension باید درون measure فراخوانی چون در غیر این صورت موجب بروز استثنای زمان اجرا میشود.
مرحله بعد و آخر مرحله طرحبندی است. در این مرحله، والد ()child.layout را فراخوانی کرده و اندازه نهایی و موقعیت فرزند را تعیین میکند. زمانی که نمای سفارشی را پیادهسازی میکنید، در صورتی که نمای شما، نماهای فرعی دیگری داشته باشد، باید تنها متد ()onLayout را override کنید.
در نتیجه فرایند اندازهگیری مانند یک مذاکره بین یک والد و فرزند است. فرزند، عرض و ارتفاع مطلوب را محاسبه میکند اما والد کسی است که فراخوانی نهایی را برای تنظیم موقعیت و اندازه فرزندش انجام میدهد.

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

برای ایجاد یک نمای BatteryMeterView باید مراحل زیر را طی کنیم. یک پروژه اندروید استودیو ایجاد کرده و یک کلاس جدید به نام BatteryMeterView اضافه کنید.
آن را با استفاده از کلاس View بسط دهید و سازندهها را طوری اضافه کنید که با super مطابقت داشته باشند.
ما برای آمادهسازی رسم برخی شیءهای paint، رنگ و شکلها را اعلان میکنیم.
View-ی ما همانند یک ویجت ابتدایی باید مقداری آمادهسازی اولیه داشته باشد و همه مشخصهها نوعی مقدار پیشفرض داشته باشند. در ادامه یک شیء همراه درون BatteryMeterView ایجاد کرده و برخی ثابتها را به آن اضافه میکنیم.
پیش از رسم باتری روی صفحه، باید اندازه و موقعیت آن را بهروزرسانی کنیم. بهترین مکان برای مدیریت هر گونه تغییر اندازه درون متد onSizeChanged است. به این منظور مراحل زیر را طی کنید:
نکته: با توجه به مقاصد آموزشی این مقاله از برخی مقادیر ثابت برای حاشیه و آفست محتوا استفاده کردهایم.
اینک برای رسم BatteryMeter شروع به override کردن متد ()onDraw میکنیم.
به خاطر بسپارید که متد onDraw با بسامد 60 بار در ثانیه (fps 60) فراخوانی میشود و قرار دادن هر نوع عملیات محاسباتی سنگین و ایجاد شیء درون آن میتواند موجب کاهش عملکرد اپلیکیشن شود. برای اجتناب از این وضعیت، میتوانیم همه اشیا را درون سازندهها بسازیم و در صورت نیاز میتوانیم مشخصهها را در ادامه تغییر دهیم.
اکنون برای این که به باتری خود امکان تغییر در زمان اجرا بدهیم، باید متد ()invalidate را هر بار که حالت نما بهروزرسانی میشود فراخوانی کنیم. کاری که invalidate انجام میدهد این است که به اندروید اجازه میدهد بداند که نما خراب شده و نیاز به ترسیم مجدد دارد. لازم به ذکر است که باید مراقب باشید زیرا فراخوانی ()invalidate به تعداد زیاد موجب بروز مشکلاتی میشود.
مرحله نهایی، افزودن نمای باتری به Layout است:
به این ترتیب کار به پایان میرسد و ما اینک یک باتری سنج داریم که خودمان ایجاد کردهایم. برای مشاهده کد کامل پروژه به این لینک (+) مراجعه کنید.
پیش از جمعبندی این مقاله بهتر است مزایا و معایب پیادهسازی نماهای سفارشی را نیز مورد اشاره قرار دهیم. همانند هر فرایند پیادهسازی دیگری، همواره مزیتها و معایبی وجود دارند، اما این موارد نباید شما را از امتحان کردن آن نا امید کنند.
ظاهر/رفتار سفارشی: نمای سفارشی = سفارشیسازی. پلتفرم اندروید گسترده است، اما موارد خاصی وجود دارند که قابلیتهای نماهای اندروید نیازهای شما را برآورده نمیسازند و از این رو نمای سفارشی این فرصت را به شما میدهد که چیزی بسازید که متعلق به شما است. زمانی که نوبت به طراحی و تعامل میرسد، کنترل کاملی دارید زیرا نمای سفارشی گزینههای بینهایتی ارائه میکند.
قابلیت استفاده مجدد/قابلیت خوانایی: زمانی که اپلیکیشنهای بزرگمقیاس توسعه میدهید، قابلیت استفاده مجدد کد همواره موضوعی مورد علاقه است. زمانی که یک نمای سفارشی ایجاد میکنید، میتوانید به سادگی آن را در چند جای دیگر نیز در اپلیکیشن خود مورد استفاده قرار دهید.
عملکرد: در برخی موارد خاص، ساختن نمای سفارشی میتواند موجب مقداری بهبود عملکرد شود.
در این بخش برخی معایب نماهای سفارشی را بررسی میکنیم.
زمان و تلاش: ایجاد نماهای سفارشی کاری زمانبر است و استفاده از آن قطعاً میتواند دشوار باشد تا اینکه به کارکرد آنها آشنا شوید.
پشتکار: چند چیز وجود دارند که در زمان پیادهسازی نماهای سفارشی باید آگاه باشید. اول این که باید مطمئن باشید فونت، اندازه متن، رنگ، سایهها، هایلایت و استایل را به درستی مدیریت کردهاید. همچنین باید مطمئن شوید که نما روی همه تراکمهای نمایشی به درستی کار میکند چون زیرکلاس بوم اندروید برحسب پیکسل رسم میشود و نه DP. اگر با تصاویر کار میکنید باید به خاطر داشته باشید که نسبت تصویر، بزرگنمایی و مقیاسبندی درستی داشته باشد.
موارد زیادی که باید مدیریت شوند: همچنین باید همه انواع شنوندههای کلیک، تعاملهای کاربر، کلیکهای منفرد، دو بار کلیک، فشردن طولانی، سوایپ کردن و جابجایی را مدیریت کنید.
در این مقاله به بررسی اتفاقاتی میپردازیم که در جریان تحویل یک فرم از سوی کاربر رخ میدهد. در این نوشته بررسی خواهیم کرد که در زمان ارسال فرم HTML دادههای آن به کجا میروند و زمانی که به مقصد رسیدند چگونه میتوانیم آنها را مدیریت کنیم. همچنین به بررسی برخی دغدغههای امنیتی مرتبط با ارسال داده های فرم میپردازیم. برای مطالعه بخش قبلی این سری مطالب به لینک زیر رجوع کنید:
پیشنیاز مطالعه این نوشته سواد مقدماتی رایانه، داشتن درکی از HTML و دانش ابتدایی HTTP و همچنین برنامهنویسی سمت سرور است. هدف از این مقاله درک اتفاقاتی است که در زمان ارسال فرم رخ میدهد. بدین ترتیب ایدهای ابتدایی از شیوه پردازش دادهها در سمت سرور به دست میآوریم.
در این بخش بررسی میکنیم که وقتی دکمه ارسال فرم کلیک میشود، دادههای فرم HTML به کجا میروند؟
مفهوم وب مبتنی بر یک معماری کاملاً ابتدایی کلاینت/سرور است که آن را میتوان به صورت زیر خلاصه کرد: یک کلاینت (معمولاً مرورگر وب) درخواستی را با استفاده از پروتکل HTTP به سرور (در اغلب موارد یک وبسرور مانند Apache، Nginx ،IIS ،Tomcat و غیره) ارسال میکند. سرور با استفاده از همان پروتکل به درخواست پاسخ میدهد.

در سمت کلاینت، فرم HTML چیزی به جز روشی کاربرپسند آسان برای پیکربندی یک درخواست HTTP جهت ارسال دادهها به سرور نیست. بدین ترتیب کاربر میتواند اطلاعاتی را در درخواست HTTP تحویل دهد.
عنصر <form> شیوه ارسال دادهها را تعریف میکند. همه خصوصیتهای آن طوری طراحی شدهاند که امکان پیکربندی درخواست برای ارسال شدن در زمان کلیک روی دکمه Submit را فراهم سازند. دو مورد از مهمترین خصوصیتها action و method هستند.
این خصوصیت شیوه ارسال دادهها را تعریف میکند. مقدار آن باید یک URL معتبر باشد. اگر این خصوصیت ارائه نشده باشد، دادهها به URL صفحهای که شامل فرم است ارسال خواهند شد.
در این مثال، دادهها به یک URL مطلق یعنی (http://foo.com) ارسال میشوند:
در مثال زیر از یک URL نسبی برای ارسال دادهها به URL متفاوتی روی سرور استفاده شده است:
زمانی که مانند مثال زیر، هیچ خصوصیتی ذکر نشود، دادههای <form> به همان صفحهای ارسال میشوند که فرم در آن قرار دارد:
صفحههای قدیمیتر زیادی از نمادگذاری زیر استفاده میکنند تا نشان دهند که دادهها باید به همان صفحهای ارسال شوند که شامل فرم است. دلیل الزامی بودن این وضعیت آن است که تا HTML5 خصوصیت action الزامی بود، اما دیگر نیازی به آن ندارید.
نکته: میتوان از یک URL استفاده کرد که از پروتکل HTTPS یعنی HTTP امن بهره میگیرد. زمانی که این کار را انجام دهید، حتی اگر خود فرم روی یک صفحه ناامن میزبانی شده باشد و از طریق HTTP به آن دسترسی داشته باشید، دادهها همراه با باقی درخواست رمزنگاری میشوند. از سوی دیگر، اگر فرم روی یک صفحه امن میزبانی شده باشد، اما یک URL ناامن به صورت HTTP برای خصوصیت action وارد کنید، همه مرورگرها زمانی که کاربر دادهها را ارسال میکند، هشداری نمایش میدهند، زیرا دادهها رمزنگاری نشدهاند.
این خصوصیت شیوه ارسال شدن دادهها را تعریف میکند. پروتکل HTTP چندین روش برای اجرای درخواست ارائه میکند. دادههای فرم HTML از طریق چند روش مختلف ارسال میشوند که رایجترین آنها متد GET و متد POST هستند.
برای درک تفاوت بین دو متد فوق باید گامی به عقب برداریم و طرز کار HTTP را بررسی کنیم. هر بار که میخواهید به منبعی روی وب دسترسی پیدا کنید، مرورگر یک درخواست به آن URL ارسال میکند. هر درخواست HTTP شامل دو بخش است که یکی هدر و دیگری بدنه درخواست است. بخش هدر شامل مجموعهای از متادیتا کلی در مورد قابلیتهای مرورگر است و بخش بدنه میتواند شامل آن دسته از اطلاعات ضروری باشد که سرور برای پردازش یک درخواست خاص به آنها نیاز دارد.
متد GET از سوی مرورگر برای درخواست از سرور جهت بازگشت دادن منبع مفروض مورد استفاده قرار میگیرد. در این حالت مرورگر یک بدنه خالی ارسال میکند. از آنجا که بدنه درخواست خالی است، اگر فرم با استفاده از این متد ارسال شود، دادههای ارسالی به سرور به URL الحاق میشوند.
فرم زیر را در نظر بگیرید:
از آنجا که متد GET مورد استفاده قرار گرفته است، میبینید که در زمان تحویل دادن فرم، URL-ای به صورت زیر در نوار آدرس مرورگر ظاهر میشود.
www.foo.com/?say=Hi&to=Mom

دادههای الحاقی به URL به صورت یک سری از جفتهای نام/مقدار هستند. پس از آن که آدرس وب URL پایان یافت یک کاراکتر علامت سؤال (?) و سپس جفتهای نام/مقدار را میآوریم که هر کدام با یک کاراکتر & از هم جدا میشوند. در این مورد، ما دو بخش از دادهها را به سرور ارسال میکنیم:
بدین ترتیب درخواست HTTP به صورت زیر خواهد بود:
متد POST کمی متفاوت است. این همان متدی است که مرورگر از آن برای صحبت کردن با سرور هنگام درخواست یک پاسخ استفاده میکند. چنین درخواستی با در نظر گرفتن دادههای ارائه شده در بدنه درخواست HTTP ارسال میشود. به زبان ساده درخواست بیان میکند: «سلام سرور، به این دادهها نگاه کن و یک نتیجه مناسب به من بازگشت بده.» اگر فرم با استفاده از این متد ارسال شود، دادهها به بدنه درخواست HTTP الحاق میشوند.
زمانی که فرم با استفاده از متد POST ارسال میشود، هیچ دادهای به URL الحاق نمیشود، و درخواست HTTP نیز به صورت زیر به نظر میرسد و به جای آن دادهها در بدنه درخواست HTTP قرار میگیرند:
هدر Content-Length نشاندهنده اندازه بدنه است و هدر Content-Type نوع منابعی که به سرور ارسال میشود را نشان میدهد. در ادامه در مورد این هدرها بیشتر صحبت خواهیم کرد.
درخواستهای HTTP هرگز به کاربر نمایش پیدا نمیکنند، اگر میخواهید آنها را مشاهده کنید باید از ابزارهایی مانند Network Monitor در فایرفاکس یا Developer Tools در کروم بهره بگیرید. به عنوان مثال، دادههای فرم به صوت زیر در برگه Network کروم نمایش پیدا میکنند. پس از تحویل دادن فرم:
شما میتوانید دادههای فرم را به صورتی که در تصویر زیر نمایش یافته به دست آورید:

تنها چیزی که برای کاربر نمایش پیدا میکند، URL فراخوانیشده است. چنان که پیشتر اشاره کردیم، با یک درخواست GET کاربر دادهها را در نوار URL خود میبیند، اما با درخواست POST چنین اتفاقی نمیافتد. این وضعیت به دو دلیل میتواند مهم باشد.
اگر بخواهید یک رمز عبور (یا هر داده حساس دیگر) را ارسال کنید، هرگز نباید از متد GET استفاده کنید، چون در غیر این صورت خطر نمایش یافتن آن در نوار URL وجود دارد که کاری بسیار غیر امن است.
اگر میخواهید حجم بالایی از دادهها را ارسال کنید، متد POST ترجیح دارد، زیرا، مرورگرها محدودیت اندازه URL را دارند و اغلب سرورها نیز برای پذیرش URL محدودیت تعیین کردهاند.
هر نوع متد HTTP که انتخاب کنید، سرور رشتهای متنی دریافت خواهد کرد که آن را تجزیه میکند تا دادههایش را به صورت لیستی از جفتهای کلید/مقدار به دست آورد. روش دسترسی به این لیست به پلتفرم توسعه یا فریمورک خاصی که مورد استفاده قرار میدهید مرتبط خواهد بود. آن فناوری که استفاده میکنید روی روش مدیریت کلیدهای تکراری نیز تأثیر دارد. در اغلب موارد مقداری که اخیراً برای یک کلید، فعال بوده است ترجیح دارد.
PHP برخی اشیای سراسری برای دسترسی به دادهها ارائه میکند. با فرض این که از متد POST استفاده میکنید، مثال زیر صرفاً دادهها را میگیرد و آنها را به کاربر نمایش میدهد. البته این که با دادهها چه میکنید به شما مربوط است. میتوان دادهها را نمایش داد، آنها را در پایگاه داده ذخیره کرد، به وسیله ایمیل ارسال نمود یا به روش دیگری مورد پردازش قرار داد.
مثال زیر صفحهای را به همراه دادههای ارسالی نمایش میدهد. در فایل زیر روش استفاده عملی از آن را مشاهده میکنید:
فایل فوق شامل همان مثالی است که قبلاً دیدیم و دارای متد POST و یک action به صورت php-example.php است. زمانی که این فرم تحویل میشود، دادهها را به فایل php-example.php ارسال میکند که شامل کد PHP است که در قطعه کد فوق مشاهده میکنید. زمانی که این کد اجرا شود، خروجی مرورگر به صورت Hi Mom خواهد بود.

نکته: مثال فوق زمانی که صفحه را به صورت لوکال در مرورگر بارگذاری کنید کار نمیکند، زیرا مرورگرها نمیتوانند کد PHP را تفسیر کنند، بنابراین وقتی که فرم تحویل میشود، مرورگر صرفاً پیشنهاد میکند که فایل PHP را دانلود کنید. برای این که این مثال عملیاتی شود، باید آن را روی نوعی سرور PHP اجرا کنید. گزینههای مناسب برای تست PHP نرمافزارهای MAMP و AMPPS هستند.
مثال زیر شیوه استفاده از پایتون برای انجام کاری مشابه فوق را نمایش میدهد. بدین ترتیب دادههای تحویلی در یک صفحه وب نمایش پیدا میکنند. در این مثال از فریمورک Flask برای رندر کردن قالبها، مدیریت تحویل دادهها و موارد دیگر استفاده شده است:
دو قالبی که در کد فوق مورد ارجاع قرار گرفتهاند به شرح زیر هستند:
نکته: این کد نیز در صورتی که آن را مستقیماً در مرورگر بارگذاری کنید کار نخواهد کرد. طرز کار پایتون اندکی با PHP متفاوت است. برای این که بتوانید آن را به صورت لوکال اجرا کنید باید Python/PIP را نصب کنید. سپس باید Flask را با استفاده از دستور زیر نصب کنید:
pip3 install flask
در ادامه باید مثال را با استفاده از دستور زیر اجرا کنید:
python3 python-example.py
سپس به آدرس localhost:5000 در مرورگر خود بروید.
فناوریهای سمت سرور زیاد دیگری نیز وجود دارند که میتوانید برای مدیریت فرم استفاده کنید و شامل Perl ،Java ،.Net ،Ruby و غیره هستند. شما میتوانید هر کدام را که دوست دارید انتخاب کنید. البته لازم به ذکر است که استفاده مستقیم از این فناوریها بسیار نامعمول است، زیرا کار با آنها کمی دشوار است. به طور معمول افراد از فریمورکهای خوبی مانند لیست زیر که وجود دارند استفاده میکنند تا مدیریت فرمها را به روش آسانتر انجام دهند:
لازم به ذکر است که حتی در صورت استفاده از این فریمورکها نیز، کار با فرمها لزوماً آسان نیست. اما به هر حال نسبت به تلاش برای نوشتن همه کارکردها از صفر بسیار آسانتر است و صرفهجویی زمانی زیادی ایجاد میکند.
نکته: آموزش همه زبانها یا فریمورکهای سمت سرور خارج از حیطه این مقاله است.
ارسال فایلها با استفاده از فرمهای HTML یک حالت خاص است. فایلها دادههای باینری هستند یا دست کم این گونه تصور میشوند، در حالی که همه دادههای دیگر متنی هستند. از آنجا که HTTP یک پروتکل متنی است، الزامات خاصی برای مدیریت دادههای باینری وجود دارند.
این خصوصیت امکان تعیین مقدار هدر HTTP به صورت Content-Type را میدهد که در درخواست ایجاد شده هنگام تحویل فرم جای میگیرد. این هدر بسیار مهم است، زیرا به سرور اعلام میکند که چه نوع دادههایی ارسال میشوند. مقدار آن به صورت پیشفرض به صورت زیر است:
application/x-www-form-urlencoded
به زبان ساده معنای آن چنین است که: «این یک داده فرم است که به صورت پارامترهای URL کدگذاری شده است.»
اگر بخواهید فایلها را ارسال کنید، باید گامهای بیشتری بردارید:
برای نمونه:
نکته: برخی مرورگرها از خصوصیت multiple روی عنصر <input> پشتیبانی میکنند و بدین ترتیب میتوانید بیش از یک فایل را به وسیله تنها یک عنصر <input> برای آپلود انتخاب کنید. این که سرور چگونه این فایلها را در عمل مدیریت میکند، به فناوری مورد استفاده در سمت سرور بستگی دارد. چنان که پیشتر گفتیم، استفاده از یک فریمورک موجب میشود که کارها بسیار آسانتر شوند.
هشدار: بسیاری از سرورها با محدودیت اندازه برای فایلها و درخواستهای HTTP پیکربندی شدهاند تا از سوءاستفادههای احتمالی جلوگیری کنند. پیش از ارسال یک فایل، این محدودیت را با مدیر سرور بررسی کنید.
هر بار که دادهها را به سرور ارسال میکنید، باید بحث امنیت را در نظر داشته باشید. فرمهای HTML با اختلاف زیاد یکی از مهمترین هدفهای حملات رایج به سرورها هستند. البته این مشکلات هرگز از خود فرمهای HTML ناشی نمیشوند، بلکه ناشی از شیوه مدیریت دادهها از سوی سرور هستند.
بسته به این که میخواهد چه کار بکنید، برخی مشکلات کاملاً شناختهشده امنیتی وجود دارند که ممکن است با آنها مواجه شوید:
اسکریپتنویسی بین سایت (XSS) و جعل درخواست بین سایت (CSRF) دو نوع رایج از حملههایی هستند که در زمان نمایش دادههای ارسالی یک کاربر به کاربر دیگر رخ میدهند.
حمله XSS به مهاجم امکان میدهد که اسکریپتهای سمت کلاینت را در صفحههای وب مشاهدهشده از سوی کاربران دیگر تزریق کند. آسیبپذیری اسکریپتنویسی بین سایت از سوی مهاجمان برای دور زدن کنترلهایی مانند «سیاست ریشه یکسان» (same origin policy) را فراهم میسازد. تأثیر این حملهها متفاوت است و از یک مزاحمت ساده تا ریسکهای امنیتی مهم متغیر خواهد بود.
حملههای CSRF مشابه حملههای XSS هستند، چون به روش مشابهی آغاز میشوند و اسکریپت سمت کلاینت در صفحههای وب تزریق میشود، اما هدف آنها متفاوت است. مهاجمان CSRF تلاش میکنند تا دسترسیهای مربوط به کاربران با دسترسی بالاتر (مانند مدیر سایت) را به دست آورند تا اقداماتی را که نباید انجام دهند به اجرا درآورند. برای مثال بدین ترتیب میتوانند دادهها را به یک کاربر غیر معتمد ارسال کنند.
حملههای XSS از اعتمادی که یک کاربر به وبسایت دارد سوءاستفاده میکنند، در حالی که حملههای CSRF از اعتمادی که وبسایت به کاربران خود دارد سوءاستفاده میکنند.
برای جلوگیری از این حملهها، باید همواره دادههایی که یک کاربر به سرور ارسال میکند (در صورت نیاز به نمایش دادن) را بررسی کنید و تلاش کنید محتوای HTML ارائهشده از سوی کاربر را نمایش ندهید. به جای آن باید دادههای ارائهشده از سوی کاربر را پردازش کنید تا به صورت خام نمایش پیدا نکند. تقریباً همه فریمورکهای موجود امروزه یک فیلتر کمینه پیادهسازی کردهاند که عناصر <script> ،<iframe> و <object> مربوط به HTML را از دادههای ارسالی از سوی هر کاربر حذف میکنند. بدین ترتیب ریسک کاهش پیدا میکند، اما لزوماً رفع نمیشود.
تزریق SQL نوعی از حمله است که در آن مهاجم تلاش میکند اقدامی را روی یک پایگاه داده مورد استفاده از سوی وبسایت هدف اجرا نماید. این نوع حمله عموماً شامل ارسال یک درخواست SQL به امید اجرا شدن آن از سوی سرور است. این اتفاق معمولاً زمانی رخ میدهد که سرور اپلیکیشن تلاش میکند دادههای ارسالی از سوی کاربر را در پایگاه داده ذخیره کند. در واقع این نوع حمله یکی از اصلیترین نوع حملات در برابر وبسایتها محسوب میشود.
عواقب چنین حملاتی میتواند فاجعهآمیز باشد و از فقدان دادهها تا حملاتی برای به دست آوردن کنترل زیرساخت وبسایت از طریق کسب دسترسیهای مورد نیاز متفاوت است. این یک تهدید بسیار جدی است و شما هرگز نباید دادههای ارسالی از سوی کاربر را بدون اجرای نوعی مراحل پاکسازی در پایگاه داده ذخیره کنید.
این نوع از حملهها زمانی رخ میدهند که اپلیکیشن هدرهای HTTP یا ایمیلها را بر مبنای دادههای وارد شده از سوی کاربر در یک فرم میسازد. این حمله به صوت مستقیم به سرور شما آسیب نمیزند و کاربران را نیز متأثر نمیسازد، اما یک درِ باز برای مشکلات عمیقتری مانند سرقت «نشست» (Session) یا حملههای فیشینگ فراهم میکند.
این نوع حملهها عموماً خاموش هستند و میتوانند سرور شما را به یک زامبی تبدیل کنند.
اینک سؤال این است که چگونه میتوان با این تهدیدها مبارزه کرد؟ پاسخ دادن به این سؤال نیاز به یک راهنمای کاملاً مفصلی دارد، اما به طور خلاصه چند قاعده وجود دارد که باید در ذهن خود داشته باشید. مهمترین قاعده این است که هرگز به کاربرانتان که شامل خود شما نیز میشود اعتماد نکنید. حتی یک کاربر معتمد هم میتواند به سرقت رفته باشد.
همه دادههایی که به سرور میآیند باید بررسی و پاکسازی شوند. هیچ استثنایی در این خصوص وجود ندارد.
با پیروی از سه قاعده فوق از بسیاری از مشکلات امنیتی که ممکن است پیش آید جلوگیری میکنید، اما اجرای یک بررسی امنیتی از سوی شخص ثالث همواره ایده خوبی تلقی میشود. همواره این را در نظر داشته باشید که شما شخصاً نمیتوانید همه مشکلات بالقوه را ببینید.
چنان که مشاهده کردید، ارسال دادههای فرم کار آسانی است، اما امن سازی یک اپلیکیشن چنین نیست. کافی است به خاطر بسپارید که یک توسعهدهنده فرانتاند تنها کسی نیست که مدل امنیتی دادهها را تعریف میکند. چنان که در این مقاله دیدیم امکان اجرای اعتبارسنجی دادهها در سمت کلاینت وجود دارد، اما سرور نمیتواند به این اعتبارسنجی اعتماد کند، زیرا هیچ راهی برای دانستن این که در سمت کلاینت چه اتفاقی رخ داده است ندارد.
«عبارتهای منظم» (Regular Expressions) که اصطلاحاً regex یا regexp نامیده میشوند در زمان استخراج اطلاعات از هر متنی کاملاً مفید هستند. این عبارتها برای جستجو و یافتن مطابقت یک یا چند الگوی جستجوی خاص مورد استفاده قرار میگیرند. بدین ترتیب میتوان توالی خالصی از کاراکترهای ASCII یا یونیکد را یافت. زمینههای کاربرد regex از اعتبارسنجی تا تجزیه/جایگزینی رشتهها، ترجمه دادهها به قالبهای دیگر و وب اسکرپینگ متفاوت است.
یکی از جالبترین قابلیتهای Regex این است که پس از یادگیری ساختار آن میتوانید در تقریباً همه زبانهای برنامهنویسی شامل JavaScript ،Java ،VB ،C# ،C / C++ ،Python ،Perl ،Ruby ،Delphi ،R ،Tcl و بسیاری دیگر استفاده کنید. تنها تفاوت در این است که برخی زبانها از برخی قابلیتهای پیشرفتهتر و نسخههای ساختار متفاوت پشتیبانی میکنند.
در ادامه برخی نمونهها را مورد بررسی قرار داده و در مورد هر کدام توضیح میدهیم.
در این بخش برخی موضوعات کاملا مقدماتی مرتبط با regex را مورد بررسی قرار میدهیم.
باید از عملگر (.) با احتیاط استفاده کنید، چون در اغلب موارد کلاس یا کلاس کاراکتر منفی آن (که در ادامه معرفی میکنیم) سریعتر و بسیار دقیقتر است. حالت نفی d ،\w\ و s\ به ترتیب D ،\W\ و S\ هستند. برای نمونه D\ تطبیق معکوس را با توجه به آن چیزی که با d\ به دست میآید ارائه میکند.
برای این که به صورت عملی از آن استفاده کنید، باید کاراکترها را با استفاده از \ به صورت escape درآورید چون معنای خاصی دارند.
توجه کنید که میتوانید کاراکترهای غیر قابل چاپ مانند t\، خطوط جدید n\، بازگشتهای carriage یعنی r\ را نیز تطبیق دهید.
تا به این جا در مورد شیوه ساخت یک regex مطالبی آموختیم، اما یک مفهوم بنیادی به نام فلگ را فراموش کردهایم.
Regex معمولاً به صورت /abc/ ارائه میشود که الگوی جستجو به وسیله دو کاراکتر اسلش متمایز شده است. در انتهای عبارت منظم میتوان یک فلگ با مقادیر زیر تعیین کرد (امکان ترکیب کردن آنها با هم نیز وجود دارد):
در این بخش برخی مباحث regex را بررسی میکنیم که فراتر از سطح مقدماتی قبلی هستند، اما هنوز وارد حیطه پیشرفته نشدهایم.
این عملگر در مواردی که لازم است اطلاعات از رشتهها یا دادهها با استفاده از زبان برنامهنویسی خاصی استخراج شود کاملاً مفید خواهند بود. هر رخداد چندگانه به وسیله چند گروه به دست میآید و در یک آرایه کلاسیک عرضه میشود، یعنی میتوان با استفاده از یک اندیس روی نتیجه تطبیق به هر رخداد مجزا دسترسی داشت.
اگر بخواهیم برای گروههای خود نامگذاری کنیم، میتوانیم مقادیر گروه را با استفاده از نتیجه تطبیق یافته مانند یک دیکشنری به دست آوریم که در آن کلیدها نام هر گروه خواهند بود.
به خاطر داشته باشید که درون عبارتهای براکتی، همه کاراکترهای خاص (شامل بک اسلش \) قدرت خاص خود را از دست میدهند. بدین ترتیب قاعده scape قابل استفاده نیست.
سورها یعنی * + {} عملگرهای حریص (Greedy) هستند، بنابراین مورد تطبیق را تا آنجا که ممکن است روی متن مورد نظر گسترش میدهند.
برای نمونه <+.> با <div>simple div</div> در عبارت This is a <div> simple div</div> test تطبیق مییابد. برای این که تنها تگ div تطبیق یابد، میتوانیم از یک ? استفاده کنیم تا آن را تنبل (Lazy) کنیم:
توجه داشته باشید که یک راهحل بهتر عدم استفاده از . به نفع یک regex صریحتر است:
در این بخش، برخی مباحث پیشرفته مرتبط با regex را مورد بررسی قرار میدهیم.
babc\b\ – یک جستجوی «صرفاً کل کلمه» اجرا میکند.
b\ نشاندهنده یک مهار مانند caret است (مشابه $ و ^ است) و با موقعیتهایی تطبیق مییابد که یک طرف یک کاراکتر کلمه (مانند w\) است و طرف دیگر کاراکتر کلمه نیست، مثلاً میتواند ابتدای یک رشته یا یک کاراکتر فاصله باشد.
حالت نفی این کران به صورت B\ است. این کران با همه موقعیتهای تطبیق مییابد که b\ تطبیق پیدا نکند و میتوان از آن برای یافتن الگوی جستجویی استفاده کرد به طور کامل در کاراکترهای کلمهای احاطه شده است.
Babc\B\ تنها در صورتی تطبیق مییابد که الگو به صورت کامل در کاراکترهای کلمه احاطه شده باشد.
([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 میتوانند کاملاً متنوع باشند. مطمئناً شما دست کم یکی از این وظایف را تاکنون در زمان اجرای وظایف توسعه نرمافزار خود مشاهده کردهاید. در ادامه این کاربردها را در یک فهرست خلاصه جمعبندی کردهایم.
منبع" فرادرس
در این مقاله به بررسی ترفندهای مفید و مهم زبان برنامهنویسی پایتون میپردازیم. شما با بهرهگیری از این ترفندهای پایتون میتوانید در زمان و تلاش خود صرفهجویی زیادی بکنید.
با استفاده از روش معرفیشده در مثال زیر میتوانید آیتمهای آرایه را برحسب نامشان مورد دسترس قرار دهید:
در مثال زیر با روشی آشنا میشوید که امکان تعویض متغیرها را فراهم میسازد:
با استفاده از کتابخانههای معرفیشده در کد زیر میتوانید به برخی آمار مرتبط با کد پایتون خود دسترسی پیدا کنید:
شاید برخی اوقات لازم باشد که یک رشته را به مقدار معینی تکرار کنیم. البته انجام این کار در مورد تعداد پایین به صورت دستی بهصرفهتر است، اما زمانی که تعداد بالا باشد، میتوانید از روش زیر برای تکرار رشته کمک بگیرید:
با استفاده از روش ساده معرفیشده در کد زیر میتوانید یک رشته را به هر نوع و طولی که دوست دارید برش دهید:
در کد زیر مثالی از روش معکوس سازی یک رشته را مشاهده میکنید:
اگر میخواهید از آخرین کاراکتر یک رشته آغاز کنید، میتوانید از اندیس منفی استفاده کنید:
برای این که بتوانید آیتمهای مشترک دو مجموعه را به دست آورید، میتوانید از کد زیر استفاده کنید:
برای به دست آوردن اختلاف بین مجموعهها، یعنی آیتمهایی که در اشتراک دو مجموعه نیستند به روش زیر عمل میکنیم:
برای به دست آوردن ترکیب صریح دو مجموعه از کد زیر استفاده میکنیم:
با ارائه یک مقدار پیشفرض برای یک آرگومان میتوان آرگومانهای اختیاری ارسال کرد:
اگر تابع شما میتواند هر تعداد آرگومان دریافت کند در این صورت میتوانید یک کاراکتر ستاره (*) در ابتدای نام پارامتر قرار دهید:
با بهرهگیری از arguments** میتوان تعداد متغیری از آرگومانهای کلیدواژه را به یک تابع ارسال کرد. بدین ترتیب میتوان مقادیر دیکشنری را به عنوان آرگومانهای کلیدواژه ارسال کرد:
اگر تابعی الزام به بازگشت مقادیر چندگانه داشته باشد، در این صورت:

با استفاده از روش زیر میتوانید حلقههای تکخطی بنویسید:
در مثال زیر روشی مشاهده میکنید که:
فرایند garbage collection دستی را میتوان به صورت زمانبندیشده یا با سازوکاری مبتنی بر رویداد اجرا کرد:
مثال زیر شیوه نمایش نام تابع را نمایش میدهد. این تنها یک کد مثالی است که نشان میدهد چگونه میتوانیم یک دکوراتور را فراخوانی کنیم. میتوان از دکوراتورها برای فراخوانی لاگرها، اجرای عملیات امنیتی و موارد دیگر استفاده کرد.
اکنون از آن در تابع خود استفاده میکنیم:
در کد زیر با روش Unzip کردن آشنا میشویم:
در این بخش با کد مرتبط با روش الحاق دو کالکشن مختلف با هم آشنا میشویم:
در برخی موارد لازم است که مستقیماً به آدرس حافظه یک متغیر دسترسی داشته باشیم:
برای نمایش دایرکتوری کنونی که در آن قرار دارید، میتوانید از کد زیر استفاده کنید:
برای دیدن این که کدام ماژولها در برنامه ایمپورت شدهاند میتوانید از کد زیر استفاده کنید:
برای این که شناسه پردازش جاری را به دست آورید میتوانید از کد زیر استفاده کنید:
بدین ترتیب به پایان این مقاله میرسیم. امیدواریم از مطالعه این نکات و ترفندهای زبان برنامهنویسی پایتون لذت برده باشید.