Pattern ها در لوا :
برای مشاهده این لینک/عکس می بایست عضو شوید ! برای عضویت اینجا کلیک کنید
پترن ها ، رشته هایی هستند که در برخی از آرگومان توابع مربوط به رشته یا string در لوا بکار میروند و برای پیدا کردن کاراکتر یا مجموعه ای از کاراکترهای خاص در یک رشته ، کاربرد دارند مثلا اگر بخواهیم فقط کاراکترهای مربوط به عدد را در رشته ای که ترکیب کاراکتر عدد و حروف و کاراکترهای سجاوندی دارند ، بیابیم به راحتی با استفاده از پترن ها ، انجام شدنی است . یک بخش بسیار مهم برنامه نویسی ، قابلیت کاربر در دستکاری و بازی و قابلیت انعطاف پذیری در رشته هاست. در این میان ، پترن ها ، نقش بسیار مهم و تاثیر گذاری را برای برنامه نویس ایجاد میکنند تا بتواند بسیار منعطف تر با رشته ها کار کند بنابراین پترن ها نه تنها در کار با رشته ها تاثیر خیلی مهمی دارند ، بلکه حتی میتواند در قالب کلی و نتیجه ی یک نرم افزاری که برنامه نویس مینویسد ، بسیار تاثیر گذار باشد
اینکه پترن ها را چگونه بکار میروند و قضیه اش چیست را در ادامه بررسی میکنیم اما در ابتدای کار باید بدانیم که پترن ها را کجا و در کدام توابع میتوانیم بکار ببریم؟
در هر آرگومانی از توابع string که در توضیح آن (در سایت اصلی لوا) عبارت pattern آمده باشد ، میتوانیم از پترن ها استفاده کنیم . مثلا در آرگومان دوم تابع string.find و string.gmatch و string.gsub و string.match
توابع بالا ، کاربردشان به ترتیب ، string.find برای پیدا کردن یک رشته (یا پترن) در رشته ی مورد نظر هست و بسیار بسیار مهم است . هر چند 3 تابع دیگر قابلیت کار با پترن ها را دارند ولی عملکرد این تابع (که برا پیدا کردن رشته هست) باعث میشود که این تابع قابل درک و ساده تر باشد مثال هایش و اکثر تمرین های این آموزش ، با مثال از این تابع هست و پترن در توابع دیگر ، مثل این تابع هستند . تابع string.gmatch ، تابع iterator ای است که در هر بار فراخوانی ، پترن مورد نظر را در رشته ی مورد نظر جستجو میکند (شبیه عملکرد تابع find را دارد) . تابع string.gsub هم در رشته ای ، رشته یا پترن مورد نظر را جایگزین میکند
علاوه بر اینها ، با توابع string.char که کد اسکی (عدد) مورد نظر را میگیرد و کاراکتر آنرا برمیگرداند و تابع string.byte را که دقیق برعکس تابع string.char عمل میکند و تابع string.sub هم که تکه ی مورد نظر از رشته را برش میدهد و برمیگرداند هم اندکی کار میکنیم (تابع string.sub که رشته را برش میدهد با تابع string.gsub که رشته یا پترن را جایگزین نمونه ی آن در رشته میکند ، اشتباه گرفته نشود)
بگذارید ابتدا آرگومان و خروجی توابع string.find را بررسی کنیم (همانطور که میدانیم ، این تابع برای جستجوی یک پترن یا رشته داخل رشته ی مورد نظر هست) :
آرگومان اول این تابع ، رشته ی مورد نظر که میخواهیم پترن را در آن جستجو کنیم ، قرار میدهیم
در دومین آرگومان ، پترن یا رشته ای را که میخواهیم در رشته ای که در آرگومان اول دادیم ، جستجو شود را مینویسیم
در سومین آرگومان که اختیاری هست ، عددی را وارد میکنیم مبنی بر اینکه در رشته ی مورد نظرمان (که در آرگومان اول وارد کردیم) از چندمین کاراکتر ، شروع به جستجو کردن کند (بصورت پیش فرض ، از کاراکتر اول از رشته شروع به جستجو میکند)
آرگومان چهارم هم اختیاری و بولین هست و پیشنهاد میشود که مقدار این آرگومان داده نشود
مقدار خروجی این تابع ، در صورتی که رشته ی جستجو شده ، پیدا نشود ، مقدار nil برگردانده میشود در همه ی متغییرهای خروجی این تابع . اگر مقداری پیدا شود ، در متغییر اول آن ، عدد اولین کاراکتری که پیدا شد و در متغییر دوم آن ، عدد آخرین کاراکتری که پیدا شد ، ذخیره میشود . در صورتی که در پترن (دومین آرگومان این تابع) ، از capure ها (که علامت پرانتز دارند و بعدا آشنا میشویم) استفاده شود ، خود آن رشته ای که پیدا شد ، به ترتیب در متغییرهای متوالی ذخیره میشود یعنی اولین capture (رشته ی داخل پرانتز) در سومین متغییر این تابع و دومین capture (رشته ی داخل پرانتز) در چهارمین متغییر این تابع و غیره ... ذخیره میشوند
برای مشاهده این لینک/عکس می بایست عضو شوید ! برای عضویت اینجا کلیک کنید
تابع string.sub (که رشته ای را برش میدهد) :
در اولین آرگومان ، رشته ای را که میخواهیم برش دهیم را وارد میکنیم
در دومین آرگومان ، عدد کاراکتری که میخواهیم شروع برش از آنجا شروع شود
و در آخرین آرگومان ، عدد کاراکتری که پایان برش به آنجا ختم میشود
به عنوان خروجی ، اگر با خطا مواجه بشویم ، nil وگرنه رشته ی برش داده شده را برمیگرداند
مثال ها:
1)
مشخص است که در خط دوم ، تابع string.find ، رشته ای را که در آرگومان دوم یعنی "padvish" را در رشته ای که در متغییر Str ذخیره شده ، جستجو و در صورت پیدا کردن ، شماره ی اولین کاراکتر پیدا شده را در متغییر اولی که OneChar هست ذخیره میکند (که در مثال بالا برابر 1 میشود چون رشته ی "padvish" در رشته ی مورد نظر ، از کاراکتر اول شروع شده و تا کاراکتر هفتم ادامه میابد) بنابراین در متغییر LastChar ، شماره ی مربوط به آخرین کاراکتر پیدا شده که 7 میباشد ، ذخیره میشود (دقت کنید اگر تابع string.find( چیزی پیدا نکند ، در اولین متغییر آن یعنی OneChar در مثال بالا ، nil مقداردهی میشود)کد:Str = "padvish eps 1.7" OneChar, LastChar= string.find(Str, "padvish") CutedStr = string.sub(Str, OneChar,LastChar) Dialog.Message("Notice", "First Charracter : "..OneChar.."\nLast Charracter : "..LastChar.."\nCutted Charracters : "..CutedStr , MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
متغییر string.sub هم که شماره ی کاراکتر داده شده (کاراکتر آغاز که متغییر OneChar و پایانی که LastChar هست) در آرگومان 2 و 3 ی این تابع را از رشته ی مورد نظر که در آرگومان اول داده میشود ، جدا میکند و در متغییر CutedStr میریزد پس در رشته ی Str ، اولین تا هفتمین کاراکتر که میشود رشته ی "padvish" ، جدا میشود و در متغییر CutedStr به عنوان رشته ذخیره میشود
اما قرض از طرح این سئوال ساده ، نکته ی بسیار ساده و در وهله ی اول بسیار ابتدایی ست اما بسیار اساسی برای درک مفاهیم بعدی . آن هم اینکه در آرگومان دوم تابع string.find که رشته یا پترن مورد نظر (پترن که همان از نوع رشته ای هست) را برای جستجو (در رشته ی آرگومان اول) مینویسیم ، دقیقا کاراکتر به کاراکتری که مینویسیم ، اگر دقیقا و آن هم به ترتیب همان کاراکترها (حتی حساس به حروف کوچک و بزرگ) را در رشته ی آرگومان اول پیدا کرد ، نتیجه حاصل میشود یعنی در مثال بالا ، اگر تابع string.find ، در رشته ی Str ، به ترتیب حروف p و بعد از آن a و بعد از آن d و بعد از آن v و بعد از آن i و بعد از آن s و بعد از آن h را آنهم با همه ی حروف کوچک یافت ، پس تابع string.find نتیجه ای را حاصل میکند و مقداری را بازمیگرداند
یعنی الان اگر در آرگومان دوم این تابع ، بنویسیم "padvish eps" حاصل ایجاد میشود (مقدار OneChar برابر 1 و مقدار LastChar برابر 11 میشود) ولی اگر کوچکترین دستبردی در آن ببریم ؛ مثلا یکی از کاراکترها را با حروف بزرگ در آرگومان دوم بنویسیم یعنی مثلا بنویسیم "padvish Eps" چون در رشته ی Str دقیق مثل آن پیدا نشد یعنی حرف "E" با حرف بزرگ نوشته نشد ، مقداری را این تابع برنمیگرداند و نتیجه حاصل نمیشود یا مثلا اگر بجای یک فضای خالی بین padvish و eps از دو فضای خالی استفاده کنیم یا کمتر کنیم و یا هر کاراکتر دیگری و در کل هر دستکاری ای بجز دقیق خود همان متن کنیم تنها در صورتی نتیجه حاصل میشود که دقیق و عدل کپی همان چیزی که نوشته شد ، باشد ، مقدار حاصل میشود اما چون این گونه نیست (مثلا استفاده از دو فضای خالی بین padvish و eps بجای یکی) پس نتیجه ای حاصل نمیشود و مقدار اولین متغییرش nil میشود
کلاس های کاراکتر :
کلاس های کاراکتر همان به عنوان پترن ها شناخته شده هستند. در واقع کاراکترهایی هستند که اغلب با علامت % شروع میشوند و هر کدام ، نمونه های خاصی از یک رشته (در صورت وجود آن نمونه یا پترن در داخل رشته) را برمیگردانند مثلا فقط عددها در رشته ی مورد نظر یا فضاهای خالی (space) یا حروف ها و ...
در لیست زیر ، انواع کلاس های کاراکتر ها (پترن ها) و معنای آن ها در رشته های پترن بیان میشود :
. ----> کاراکتر نقطه (دات) هست و همه ی کاراکترها را برمیگرداند
a% ----> کاراکترهای تحت عنوان letter (همان حروف بدون هیچ کاراکتر سجاوندی یا space یا عددو ...) را برمیگرداند
c% ----> کاراکترهای کنترلی را برمیگرداند
d% ----> کاراکترهای عدد (digit) را برمیگرداند
l% ----> کاراکترهایی که حروف کوچک هستند (lower) را برمیگرداند
p% ----> کاراکترهای سجاوندی یا punctuation (مثل علامت ها مثلا .×÷@-_ و... البته بدون space) را برمیگرداند
s% ----> کاراکتر فضای الی یا space را برمیگرداند
u% ----> کاراکترهایی که حروف بزرگ هستند (upper) را برمیگرداند
w% ----> کاراکتر الفبایی (شامل حروف و عدد) را برمیگرداند
x% ----> عددهای شانزده دهی را برمیگرداند
تذکر مهم : هر کدام از این کلاس کاراکتر ها (پترن ها) ، در حالتی که مثل بالا یعنی تنها بکار روند ، فقط یک کاراکتر را برمیگردانند
تذکر مهم : هر کدام از این کلاس کاراکترها اگر بصورت حروف بزرگ نوشته شوند ، نقیض خود را جستجو میکنند یعنی مثلا D% که با حروف بزرگ نوشته شد ، یعنی همه ی کاراکترها بجز کاراکترهای عددی یا W% یعنی همه ی کاراکترها بجز کاراکترهای الفبایی (یعنی بجز کاراکترهای حروف و عدد) و ...
2)
خوب دقت کنید که از این به بعد به بحث های اصلی وارد میشویمکد:MainStr = "this year is 2015 (christian)" i, j = string.find(MainStr, "%d") CutedStr = string.sub (MainStr, i , j) Dialog.Message("Notice", CutedStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
همانطور که گفته شد ، پترن ها رشته هستند پس در خط سوم تابع string.find در آرگومان دوم آن ، باید پترن d% را در یک رشته بیاوریم و این گونه بنویسیم "d%"
از این به بعد اگر من در توضیحاتم ، چه داخل رشته نوشتم و چه برای تسریع در توضیحات ، داخل علامت رشته نگذاشتم ، درست آن در قسمت کدهاست و داخل علامت رشته
"d%" در رشته ی MainStr جستجو میکند و هر گاه در آن عددی پیدا کرد ، فقط اولین کاراکتر آن عدد را برمیگرداند یعنی در مثال بالا ، در خط سوم و در متغییر CutedStr ، فقط عدد 2 (که اولین کاراکتر عدد 2015 در رشته ی اصلی هست) ذخیره میشود نه اینکه کل عدد 2015 ذخیره شود
نکته: مشخص است اگر بخواهیم در مثال باالا ، کل عدد (یعنی کل عدد 2015) را برگردانیم (بجای فقط اولین کاراکتر آن عدد) ، بجز روش استفاده از علامت ها که در ادامه گفته میشود و بهترین روش است، باید در حلقه ی تکراری ، دانه دانه ی عضوها را بگیریم و داخل یک آرایه بریزیم و عضوهای آن آرایه را بهم متصل کنیم
3)
دقت کنید که در این آموزش ، اغلب مثال ها بسیار شبیه هم هستند اما یا پترن ها یا رشته ی اصلی آن ها متفاوت هستکد:MainStr = "this year is 2015 (christian)" i, j = string.find(MainStr, "is%s%d") CutedStr = string.sub (MainStr, i , j) Dialog.Message("Notice", CutedStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
تفاوت مثال شماره ی 3 و 2 ، در قسمت پترن آن است (از این به بعد تفاوت ها گفته نمیشو.د برای اتلاف وقت)
در این پترن آمده "is%s%d"
یعنی هرگاه ابتدا کاراکتری بنام i و حتما بعد از آن کاراکتر s و حتما بعد از آن s% که همان فضای خالی میشود (فقط یک کاراکتر از فضای خالی آمد) و بعد از آن هم d% در رشته ی اصلی آمد (فقط یک کاراکتر از عدد را میگیرد)، آنرا پیدا کن که در رشته ی اصلی هم اینرا میبینیم که کلمه ی is آمد و بعد از آن فضای خالی آمد و بعد از آن هم عدد آمد پس در متغییر CutedStr رشته ی "is 2" ذخیره میشود
پس آنهایی که معنای خاص دارند ، اغلب بعد از علامت % هستند (علائم دیگر بعدا گفته میشود ولی مهمترین همین هست) و بعد و قبل آن هر کاراکتری که آمد (مثلا در پترنو آرگومان دوم تابع string.find که کاراکتر is قبل از s% آمد و یا حتی اگر بعد از آن میامد) به عنوان کاراکتر معمولی در رشته ی اصلی یافت میشوند و معنای خاصی ندارند مثل مثال اول
** در مثال شماره ی 3 اگر در پترن ، عبارت s% را نمیگذاشتیم (یعنی این گونه مینوشتیم "is%d" ) به این معناست که بعد از کاراکتر i و s ، دقیقا بعد آن و بدون هیچ کاراکتر دیگری ، کاراکتری از نوع عدد بیاید و چون بعد از کاراکتر s ، کاراکتر عددی نیامد و کاراکتر space یا فضای خالی آمد ، پس نتیجه ای حاصل نمیشود و مقدار nil در متغییر i ذخیره میشود که باید خوب به این موضوع دقت کنیم
** در مثال بالا کلا بجای هر کاراکتری ، کاراکتری بیاوریم که ترتیب پترن ها را بهم بریزد ، نتیجه ای حاصل نمیشود یعنی اگر بجای d% یک کاراکتر غیر عدد بیاوریم یا بجای s% یک کاراکتر غیر از فضای خالی بیاوریم یا حتی جابجایی در این کاراکترها در رشته ی اصلی انجام بدهیم یا کاراکتر is در رشته ی اصلی قبل از این کاراکترها نیاید
علامت ها در پترن ها:
به این علامت ها magic کاراکتر هم گفته میشود و در عین سادگی ، بسیار نقش مهمی در پترن ها دارند
+ ---> علامت مثبت هست که کاربردش این هست که اگر بعد از پترن یا کلاس کاراکتری (منظور همان کلاس کاراکترهایی که در بالا گفته شد مثل d% و ... ) بیاید (قبل از آن نه) بجای اینکه کاراکتر را دانه دانه برگرداند ، کل آن نوع کاراکتر را از اولین کاراکتری از همان نوع که شروع شد و تا آخرین کاراکتری که از همان نوع ختم میشود را برمیگرداند و علامت بسیار کاربردی هست
علامتهای + و * و ? هم هستند که ابتدا بگذارید تقریبی با علامت + که اندکی آشنا شدیم ، بعد به این علامت ها میپردازیم
4)
تفاوتش با مثال 3 فقط در آرگومان دوم خط 2 هست که آخر آن و بعد از کلاس کاراکتر d% علامت + آمد و این به این معناست که بجای اینکه فقط تک تک این کاراکترها را هر بار جداگانه پیدا کنی (که باید در این صورت در حلقه ای آرایه های آنرا به هم پیوند دهیم و کار دشواریست) ، هر گاه بعد از space کاراکتر ، کاراکتر عددی را پیدا کردی ، از ابتدای آن کاراکتر عددی (که 2 هست) تا انتهای آن کاراکتر عددی (که 5 هست) را یکجا برگردان. پس نتیجه میشود "is 2015"کد:MainStr = "this year is 2015 (christian)" i, j = string.find(MainStr, "is%s%d+") CutedStr = string.sub (MainStr, i , j) Dialog.Message("Notice", CutedStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
دقت کنید که چون بعد از آخرین کاراکتر عددی در رشته ی اصلی که 5 (آخرین عدد 2015) هست ، کاراکتری از نوع دیگر که کاراکتر space هست آمد ، پس حتی اگر بعد از آن کاراکتر space (که بعد از 5 آمد) ، کاراکترهای عددی دیگری میآمد دیگر +d% باز هم فقط کاراکتر ععدی 2015 را برمیگرداند چون انتهای مجموع کاراکتر عددی آن کاراکتر 5 (از عدد 2015) بود و بعد از آن نوعش غیر از عددی میشد (حتی یک کاراکتر) یا مثلا اگر بجای space هر کاراکتر غیر از عددی مثلا نقطه میامد و بعد از آن دوباره عدد میامد ، باز هم هم همین یا کاراکتر حرفی یا سجاوندی و ... میامد ، باز هم همین
در این مثال ، اگر در رشته ی اصلی ، بین is و 2015 ، بجای یک کاراکتر space ، از چند کاراکتر space استفاده کنیم ، حاصلی پیدا نمیشود چون s% موجود در پترن ، چون تنها آمد پس فقط در صورتی که یک کاراکتر space بین کاراکتر is و 2015 قرار گیرد ، نتیجه یافت میشود چون بعد از s% علامت + نگذاشتیم که از ابتدا تا انتهای کاراکتر space را شامل شود یعنی هر علامت + فقط به کلاس کاراکتر قبل از خود تعلق دارد
5)
تفاوت آن فقط در علامت + قبل از s% در آرگومان دوم خط 2 و space های بیشتر در رشته ی اصلی هست که در توضیحات آخر مثال 4 گفته شد بنابراین هر چقد کاراکتر space اگر بین کاراکترهای is و عدد (2015) بگذاریم (نه کاراکتری از نوع دیگر بین شان بذاریم) کل آن فضای خالی ، به حساب میاید (از ابتدای فضای خالی تا انتهای آن) اما اگر در مثال شماره ی 4 حتی یک کاراکتر فضای خالی ، اضافی تر میگذاشتیم در رشته ی اصلی ، نتیجه ای یافت نمیشد و nil برگردانده میشدکد:MainStr = "this year is 2015 (christian)" i, j = string.find(MainStr, "is%s+%d+") CutedStr = string.sub (MainStr, i , j) Dialog.Message("Notice", CutedStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
در مثال بالا ، "is 2015" برگردانده میشود
*** تذکر مهم : اگر در پترن ها ، بعد از علامت % کاراکتری بیاوریم بجز کلاس کاراکترها (کلاس کاراکترها همان a% و w% و d% و ... که در بالا گفته شد) ، کاراکترِ بعد از کاراکتر % را در رشته ی اصلی جستجو میکند مثلا اگر در پترن ها بنویسیم i% چون این عبارت در کلاس کاراکترها موجود نیست ، در رشته ی اصلی ، کاراکتر i را جستجو میکند و هیچ فرقی با اینکه در پترن کاراکتر i را بنویسیم برای جستجو ، با عبارت i% ندارد ولی کاربرد این % زمانی است که بخواهیم یک عبارتی که در پترن ها معنای خاصی دارد و در رشته ی اصلی آمده را جستجو کنیم مثلا کاراکتر + یا - یا ? و ... را که در پترن ها معنای خاصی دارند ولی در رشته ی اصلی بخواهیم این کاراکترها را جستجو کنیم ، در پترن قبل از این کاراکترها ، کاراکتر % را مینویسیم یعنی در پترن مینویسیم +% یعنی کلمه ی بعد از % (اگر در کلاس کاراکتر موجود نباشد) را که علامت + هست ، در رشته ی اصلی جستجو کن
البته بجز کاراکتر بک اسلش \ و علامت گیومه " که در پترن ها ، قبل از هر کدام ، یک علامت بک اسلش دیگر میگذاریم یعنی اگر در رشته ی اصلی علامت " وجود داشت ، در پترن ها برای جستجوی آن ، قبل از آن مینویسیم "\ و برای علامت \ هم در رشته ی اصلی و هم در پترن ، در هر دو جا باید از دو علامت بک اسلش استفاده کرد یعنی \\ بنویسیم (بجز این و کلاس کاراکترها ، برای کاراکترها و علامت های معنا دار در پترن ها ، قبل از آن کاراکتر از % استفاده میکنیم برای جستجو)
کاراکتررها و علامت های معنا دار در پترن ها هم عبارتند از :
( ) . % + - * ? [ ^ $
که آرام آرام با آنها آشنا میشویم
6)
همیشه به پترن ها و رشته ی اصلی و تغییرات شان در هر مثال ، خوب دقت کنیدکد:MainStr = "this year is+ 2015 (christian)" i, j = string.find(MainStr, "is%+%s%d+") CutedStr = string.sub (MainStr, i , j) Dialog.Message("Notice", CutedStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
نکته ی مهم اینجا در پترن ، عبارت +% هست . همانطور که در نکته ی بالا گفته شد ، اگر بعد از % کلاس کاراکترها نیایند (مثل s% و a% و ...) و بعد از آن علامتِ % هر کاراکتری که بیاید ، همان کاراکترِ بعد از % در رشته ی اصلی جستجو میشود که برای کاراکترهای معنادار در پترن ها برای جستجو کاربرد دارد البته بجز علامت \ و علامت گیومه "
در پترن رشته ی
آمده . یعنی اگر کاراکتر i آمد و بعد از آن کاراکتر s آمد و بعد از آن کاراکتر + آمد (چون قبل از علامت + کاراکتر % آمد پس علامت بعد از % اگر جزء کلاس کاراکترها نباشند که نیست پس علامت بعد از آن که + هست در رشته ی اصلی جستجو میشود) و بعد از آن فقط یک کاراکتر space آمد (چون بعد از s% علامت + نیامد که به معنای مجموع کاراکتر space باشد ، پس اگر بیشتر از یک کاراکتر space بیاید ، قابل قبول نیست) و بعد از آن هم عدد آمد ، مجموع آن عدد (از اول عدد تا آخرین کاراکتر از نوع عدد) ، مقدار آنرا برگردان پس مقدار "is+ 2015" برگردانده میشودکد:"is%+%s%d+"
7)
همیشه به پترن ها و رشته ی اصلی و تغییرات شان در هر مثال ، خوب دقت کنیدکد:MainStr = "this year is+ ++++++++++++++++++++2015 (christian)" i, j = string.find(MainStr, "is%+%s+%++%d+") CutedStr = string.sub (MainStr, i , j) Dialog.Message("Notice", CutedStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
پترن
اگر کاراکتر i و بعد از آن کاراکتر s و بعد از آن +% یعنی تنها یک کاراکتر + و بعد از آن مجموع کاراکتر space (چون بعد از s% علامت + که معنای مجموع آن نوع کاراکتر را دارد ، آمد) و بعد از آن ++% که اولین علامت + بعد از % یعنی اینکه این علامت بعد از کاراکتر space آمده باشد و علامت + بعدی آن به این معناست که مجموع کاراکترهای + که بعد از کاراکتر space که هر چقدر آمد (که تعداد شان بعد از کاراکتر space برابر 20 تاست) و بعد از کاراکتر + هم مجموع کاراکتر عددی را حساب کن پس متغییر CutedStr مقدار "is+ ++++++++++++++++++++2015" را برمیگرداندکد:"is%+%s+%++%d+"
8)
همیشه به پترن ها و رشته ی اصلی و تغییرات شان در هر مثال ، خوب دقت کنیدکد:MainStr = "this year is+ 2015 (christian)" i, j = string.find(MainStr, "is+%s%d+") CutedStr = string.sub (MainStr, i , j) Dialog.Message("Notice", CutedStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
این مثال ، یک مثال اشتباست به این دلیل که در پترن بعد از i و s کاراکتر + آمد و چون عبارت قبل از آن از نوع کلاس کاراکتر نیست (یعنی s% یا a% و ... نیست) و این علامت هم در پترن معنای خاصی دارد ، پس باید قبل از آن علامت % میآمد که نیامد پس در متغییر i مقدار nil برگردانده میشود و ارور میدهد
9)
همیشه به پترن ها و رشته ی اصلی و تغییرات شان در هر مثال ، خوب دقت کنیدکد:MainStr = "this year is +2015 (christian)" i, j = string.find(MainStr, "is%s++%d+") CutedStr = string.sub (MainStr, i , j) Dialog.Message("Notice", CutedStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
** هر چند این مثال درست هست و مقداری را برمیگرداند و شبیه مثال قبلی هست اما اصولی نیست و این جور نوشتن در پترن ها پیشنهاد نمیشود اما چرا؟
نکته اینجاست که در پترن عبارت ++s% آمد اولین علامت + بعد از s یعنی +s% به این معناست که بعد از کاراکتر i و s مجموع کاراکترهای space آمد و دومین علامت + بعد از s یعنی ++s% یعنی فقط یک کاراکتر + بعد از آن آمد و بعداش هم که مجموع اعداد (از اولین کاراکتر عدد تا آخرین) آمد را جستجو کن پس رشته ی "is +2015" برگردانده میشود
پس کاراکترهای معنادار در پترن ها (در اینجا کاراکتر +) حتی اگر قبل از آن علامت % هم نیاید ، بسته به مکان ای که نوشته شد ، در رشته ی اصلی ، همان کاراکتر ممکن است جستجو شوند اما احتمال اشتباه برای این جور نوشتن پترن ها بسیار بالا میرود و اصولی نیست و بهتر است اگر کاراکترهای معنادار در پترن را میخواهیم جستجو کنیم ، قبل از آن از کاراکتر % استفاده کنیم (روش اصولی) . یک نمونه استفاده از این پترن ها را دیدیم که در مثال شماره 8 ارور داد پس بهتر است از روش اصولی آن استفاده کنیم
10)
این مثال هم اشتباست. بررسی میکنیم در پترن :کد:MainStr = "this year is 2015 (christian)" i, j = string.find(MainStr, "is %d%s+%(") CutedStr = string.sub (MainStr, i , j) Dialog.Message("Notice", CutedStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
بعد از کاراکترهای i و s و بعد از آن یک کاراکتر space (دقت کنید که این بار بجای استفاده از پترن s% از خود فضای خالی در سومین کاراکترِ آرگومان دوم خط 2 استفاده شده که اگر فضای خالی استفاده شده در پترن دیقق برابر با تعداد فضای خالی موجود در رشته ی اصلی باشد ، چون کاراکترها دقیق یکسان هستند ، درست هست و در این مثال هم چون یک فضای خالی بکار برده شد ، پس در سومین کاراکتر ، یک فضای خالی گذاشتیم که درست هست . البته مشخص هست که بجای این مثل موارد قبل از s% هم میشد استفاده کرد) و تا اینجا مشکلی وجود ندارد و بعد از کاراکتر space چون d% تنها آمد و بعد از آن علامت مجموع کاراکتر هم نوع (عدد) که همان علامت + هست ، نیامد پس فقط اولین کاراکتر عدد که 2 هست را برمیگرداند (نه کل مجموع کاراکترهای عدد را که 2015 هست) و بعد از آن (یعنی بعد از 2 که تا اینجا برگرداند شد یعنی تا اینجا رشته ی "is 2" برگردانده شد) هم +s% آمد یعنی بعد از عدد 2 ، فضای خالی باشد ولی چون در رشته ی اصلی بعد از 2 باز هم کاراکتر عددی است (عدد 0 هست) نه کاراکتر space پس نتیجه ای حاصل نمیشود و مقدار nil در متغییر i برگردانده میشود
اما در همین مثال اگر بعد از d علامت + را بکار ببریم ، یعنی بنویسیم +d% چون تا آخر عدد که 5 هست را برمیگرداند و بعد از آن هم چون در پترن عبارت +s% را آوردیم و در رشته ی اصلی بعد از این عدد 5 ، فضای خالی هست (چون +s% آوردیم پس یک کاراکتر فضای خالی یا 1000 کاراکتر فضای خالی پی در پی یا بیشتر هم فرقی نمیکند باشد یا نه) و بعد از آن هم عبارت ")%" در پترن بکار برده شد چون علامت پرانتز عبارت معنا داری در پترن ها هست (که در ادامه با این علامت پرانتز آشنا میشویم) و میخواهیم آنرا در رشته ی اصلی جستجو کنیم ، قبل از آن علامت % را گذاشته شد و چون بعد از space علامت پرانتز بسته آمده ، پس مقدار رشته ی
در متغییر CutedStr ذخیره میشودکد:"is 2015 ("
11)
دقیق مثل مثال بالاست فقط با کاراکترهای اضافه ترکد:MainStr = "this year is 2015 (christian / 1394\\ solar)" i, j = string.find(MainStr, "is %d+%s+%(%a+%s/%s%d+\\") CutedStr = string.sub (MainStr, i , j) Dialog.Message("Notice", CutedStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
در قسمت پترن این ها اضافه شد پس فقط این قسمت توضیح داده میشود :
یعنی بعد از کاراکتر پرانتز بسته که در مثال بالا کار شد ، مجموع کاراکترهای حروفی که همان +a% هست و در اینجا همان christian میشود و بعد از آن هم یک فضای space و بعد از آن هم یک کاراکتر اسلش (نه کاراکتر بک اسلش که این شکلی \ بود بلکه کاراکتر اسلش که این شکلی / هست و موردی ندارد برای جستجو) و بعد از آن هم فقط یک کاراکتر space و بعد هم مجموع کاراکتر عددی و بعد از آن هم کاراکتر بک اسلش \ بود (دقت کنید همانطور که گفته شد ، برای استفاده از کاراکتر بک اسلش یعنی \ هم در رشته ی اصلی که متغییر MainStr هست و هم در پترن که آرگومان دوم تابع خط دوم هست ، حتما در هر 2 جا باید دو تا علامت بک اسلش بیاید) که در کل این مثال رشته ی "\is 2015 (christian / 1394" را برمیگرداندکد:"%a+%s/%s%d+"
دقت کنید اگر علامت پلاس را همانطور که در مثال قبل توضیح داده شد ، برای کلاس کاراکترها نگذاریم بسته به تعدادشان امکان ارور هست یعنی در این مثال برای اولین d% و دومین که a% و آخرین که باز هم d% هست ، علامت + نگذاریم ، چون همانطور که در مثال قبل گفته شد ، کاراکترها ترتیب شان بهم میخورد پس ارور دریافت میکنیم و نتیجه ای حاصل نمیشود
* پس تا بحال باید این تجربه را کسب کرده باشید که در پترن ها اولا کاراکتر به کاراکتر معنا و مفهوم دارد و دوما با وجود پیچیدگی ها ، هر چند کوچک ترین اشتباهی و یا بهم زدن ترتیب آن باعث ارور میشود اما ساده هست و مهم ترین نکته ی آن این است که هر گاه علامت % آمد ، ببینیم بعد از آن آیا کاراکتر کلاس آمد یا نه و بعد از آن مه حواسمان به کاراکترهای magic در پترن ها باشد (+ و - و * و ?) و بجز اینها هم حواسمان به کاراکترهای معنا دار در پترن ها مثل پرانتز و کلوشه و ... که گفته شد باشد
* علامت های (Magic کاراکترهای) منفی - و ضرب * و علامت سئوال ? هم باشد بعد از توضیح برخی از کاراکترها و علامتهای دیگر با آنها آشنا میشویم
Capture ها :
همان علامت های پرانتز در پترن ها هستند و همانطور که در تصویر بالا میبینید ، وقتی در پترنی ، قسمتی از پترن ، داخل پرانتز قرار گیرد ، به ترتیب و بصورت متوالی ، قسمت هایی از پترن که داخل پرانتز هستند ، در متغییرهای مورد نظر (در اینجا متغییرهای تابع string.find) ذخیره میشوند . دو متغییر اول این تابع که همانطور که در مثال های بالا کار شد ، شماره ی کاراکترها را برمیگردانند. پس یعنی قسمتی از پترنی که داخل اولین پرانتز هست ، در سومین متغییر این تابع ذخیره میشود (اگر سومین متغییر را برای این تابع تعریف کنیم) و قسمتی از پترنی که داخل دومین پرانتز هست ، در چهارمین متغییر این تابع ذخیره میشود (اگر چهارمین متغییر را برای این تابع تعریف کنیم) و همین طور الی آخر (به اولین عکس دقت کنید)
این موضوع میتواند جایگزین تابع string.sub شود و به ما در کد نویسی کمتر بسیار کمک کند
12)
در پترن مورد نظر کهکد:MainStr = "this year is 2015 (christian / 1394 solar)" i, j, CutedStr1, CutedStr2 = string.find(MainStr, "(%a+)%s+(%d+)") Dialog.Message("Notice", CutedStr1 .."\n"..CutedStr2, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
هست ، اول گمان میکنیم که پرانتزی وجود ندارد پس هر گاه +a% یعنی حروف (فقط حروف نه کاراکتری غیر از نوع حروف مثل space و عدد و سجاوندی و ...) بیاید و بعد از آن کاراکتر space بیاید و بعد از آن هم عدد ، نتیجه حاصل میشود (همانطور که آموختیم ، ترتیب مهم بود و البته چون علامت + دارند ، یعنی مجموع آن ها یعنی مجموع حروف و بعد از آن مجموع space و بعد هم مجموع عدد که اگر تکی هم بودند ، پذیرفته میشود)کد:"(%a+)%s+(%d+)"
از ابتدا چک میشود . هر چند در رشته ی اصلی ، عبارت this و یا year یک مجموع حروف هستند و اتفاقا بعد از آنها هر کدام space وجود دارند اما بعد از space از هر کدام از این حروف ها ، باز حروف دیگر وجود دارد و تنها حروفی که بعد از آن space و بعد عدد باشد رشته is هست پس در اینجا +a% همان رشته ی is و +s% همان space ها و +d% هم همان عددها که 2015 هست میشود و در کل در تابع رشته ی"is 2015" پیدا میشود اما مثل مثال های قبل ، نمایان و چاپ نکردیم در خط بعد
پس در متغییر i که اولین کاراکتر پیدا شده که همان کاراکتر i از is که شماره ی 11 هست ذخیره میشود و متغییر j که شماره ی آخرین کاراکتر که کاراکتر 5 از 2015 هست و شماره 17 را دارد ذخیره میشود (i=11 و j=17) . حالا در پترن ها ، هر قسمتی که داخل پرانتز نوشته شده بودند به ترتیب در متغییر مربوط به تابع string.find و بعد از دومین متغییرش (یعنی بعد از متغییر j که در این متغییر ، عدد ذخیره شد) مقادیر داخل پرانتز در پترن ها ، آنهم بصورت رشته ذخیره میشوند یعنی داخل اولین پرانتز در پترن ها که +a% هست ، و مقدارش در مثال بالا رشته ی "is" میشد ، در سومین متغییر تابع string.fing که CutedStr1 هست ، ذخیره میشود (در صورتی که این متغییر وجود داشته باشد) و قسمتی که داخل پرانتز نیست که همان +s% هست ، داخل هیچ متغییری ذخیره نمیشود اما وجودش برای یافتن و به اصطلاح match کردن در رشته ی اصلی تا اینکه طبق الگوی رشته و به ترتیب کاراکتر باشد ، بسیار ضروری است وگرنه نتیجه ای حاصل نمیشود و مقدار nil در اولین متغییر که i هست برگردانده میشود که در مثال های گوناگون کار کردیم و حاصل دومین قسمتی از پترن که داخل پرانتز هست یعنی +d% که مقدارش 2015 میشود در این مثال ، در متغییر بعد از آخرین متغییر (چهارمین متغییر که در اینجا همان CutedStr2 هست ذخیره میشود)
در خط آخر فقط رشته ی دلخواه خودمان را که خواستیم ، مقادیرشان را فراخوانی کردیم نه مقادیر کل پترن را یعنی پیام is و در خط پایین تر پیام 2015 را میدهد . دقت کنید چون آخر متغییرهای CutedStr1 و CutedStr2 عدد هست ، پس در خط آخر وقتی میخواهیم علامت در ادامه را که دو نقطه هست بنویسیم اگر موقع نوشتن و فراخوانی CutedStr1 در خط آخر و علامت دو نقطه ، بین شان یک فضای خالی نگذاریم ، زبان تصور میکند که در اولین نقطه ، برای عدد 1 که در آخر متغییر CutedStr هست ، اعشار گذاشتیم و نمیتواند متغییر را فراخوانی کند و ارور میدهد پس به space بین شان باید توجه داشت و یا آخر متغییر را عدد ندهیم (مثلا عدد را بین نام متغییر بدهیم یا اصلا ندهیم)
باز به این نکته اشاره میکنم که در این مثال ، فقط قسمتی از رشته ای را که پیدا کردیم را برگرداندیم نه کل آنرا آنهم در 2 متغییر جداگانه و قسمت وسط که فضای خالی بود را جزء خروجی در نمایش قرار ندادیم (هر چند در اینجا چون قسمتی که نمایش داده نشد ، فقط یک کاراکتر space بود چندان به چشم نمیاید ولی میشود بجای همین space هر نوع کاراکتر دیگر مانند عدد را در نظر نگرفت و هر چیزی را که دوست داریم ، حساب کنیم) هر چند میتوانیم کل عبارت یافته شده را با همین پرانتز در خروجی بیاوریم که مشخص و واضح است که باید کل پترن را در پرانتز قرار داد
13)
این حلقه ، رشته را تا آخر آن چک میکندکد:MainStr = "this year is 2015 (christian / 1394 solar)" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1 = string.find(MainStr, "(%d+)",LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
مثلا اگر در توابعی مانند قبل و بدون حلقه مینوشتیم ، یعنی مینوشتیم :
البته در بدنه ای مثل بدنه ی مثال های قبل (یعنی بدون حلقه) ، فقط اولین مجموعه از آن کاراکتر را که در اینجا عدد هست را برمیگرداند یعنی فقط در رشته ی اصلی ، عدد 2015 را برمیگرداند و عدد 1394 که در کاراکترهای بعدی آمد را برنمیگرداند چون با اولین سرچ ، تمام جستجو را پایان میدهد اما در این مثال که حلقه موجود هست ، تابع string.find در سومین آرگومان خود ، میگوید که از کجا (کدام شماره ی کاراکتر) باید جستجو را شروع کند که متغییر LastChar ، این شماره ی کاراکتر را در اختیار آن قرار میدهد یعنی دفعه ی اول در اجرای حلقه (مقدار اولیه ی آن در خط 2 صفر هست) ، اولین کاراکتر را جستجو میکند تا پترن را در رشته ی اصلی پیدا کند و آخرین کاراکتری را که پیدا کرده ، باز هم در دومین متغییر همین تابع string.find که همان آخرین شماره ی کاراکتر پترن پیدا شده هست و به همین نام LastChar ذخیره میکند و در اجرای بعدی حلقه ، یک کاراکتر بعد از آخرین کاراکتری که پیدا کرد را دوباره جستجو میکند تا اینکه دیگر چیزی پیدا نکند و nil برگردانده شود (اگر جوابی حاصل نشود ، nil در هر 3 متغییر تابع string.find برگردانده میشود) . همانطور که مشخص هست ، باید دقت کرد که نام کاراکتری که مقداردهی اولیه برای جستجو کردیم ، با شماره ی آخرین کاراکتر پیدا شده باید یکی باشد بنابراین ، در دفعه ی اول که عدد مورد نظر (2015) پیدا شد ، در متغییر FirstChar ، شماره ی ابتدای کاراکتری که پیدا شد ، که 14 هست ذخیره و در متغییر LastChar شماره ی آخرین آخرین کاراکتر که 17 هست و در متغییر Content1 هم عدد 2015 (به عنوان رشته) ذخیره میشود و در دومین اجرای حلقه به همین ترتیب ، عدد 32 و 35 در متغییرهای FirstChar و LastChar و در متغییر Content1 هم عدد 1394 ذخیره میشود (در سومین اجرای حلقه چون از کاراکتر 36 به بعد ، عددی در رشته ی اصلی نیامده ، پس مقدار nil برگردانده میشود و از حلقه خارج میشود)کد:FirstChar, LastChar, Content1 = string.find(MainStr, "(%d+)")
14)
همانطور که در کلاس کاراکترها بیان شد ، علامت نقطه . به معنای هر نوع کاراکتری هست (همه نوع کاراکتر) و هر کلاس کاراکتری هم که بصورت تکی (یعنی بدون علامت + بعد از آن میآمد) به معنای برگردندن فقط یک کاراکتر مورد نظر بود و از آنجایی هم که این حلقه تا آخر برای جستجوی پترن مورد نظر (که در اینجا برای یافتن همه ی نوع کاراکترهاست) اسکن میکند ، پس به تعداد کاراکترها ، هر بار یک کاراکتر را برمیگرداندکد:MainStr = "this year is 2015 (christian / 1394 solar)" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1 = string.find(MainStr, "(.)",LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
اما در همین مثال اگر در پترن ، بعد از کلاس کاراکتر .% علامت + بگذاریم یعنی بنویسیم +.% مثل بقیه ی موارد به معنای مجموع همان نوع کلاس کاراکترهاست و چون کلاس کاراکتر .% همه ی انواع کاراکتر را اشاره میکند ، پس کل رشته ی مورد نظر ، برگردانده میشود
** دقت کنیم که اگر در رشته ی اصلی ای ، بخواهیم کاراکتر نقطه را جستجو کنیم ، چون این جزء کلاس کاراکترهاست و در پترن ها معنای خاصی دارد ، باید همانطور که توضیح داده شد ، قبل از نقطه ، علامت % را بگذاریم یعنی بنویسیم .%
15)
در پترن مربوط به این مثال ، ابتدا برای d% دنبال عدد میگردد و عدد 2 (که همان ابتدای عدد 2015 هست) را مییابد و چون علامت + بعد از آن نیامد پس فقط منظور یک کارکتر و آنهم اولین کاراکتر عددی هست که پیدا میشود (که همان 2 هست) و بعد از آن +. آمد که یعنی بعد از عدد 2 ، همه ی کاراکترهای بعد از آن را انتخاب کن و بعد از آن هم s% آمد یعنی آخرین محتوای +. تا جایی ادامه یابد که قبل از آخرین کاراکتر فضای خالی باشد (که در اینجا آخرین کاراکتر فضای خالی ، بین عدد 1394 و حروف solar هست) بنابراین عبارت +. در پترن بعد از حرف 2 و قبل از آخرین فضای خالی هست و چون فقط این قسمت در پرانتز آمد پس فقط این قسمت در متغییر Content1 ذخیره میشود بنابراین مقدار آن برابر رشته یکد:MainStr = "this year is 2015 (christian / 1394 solar)" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1 = string.find(MainStr, "%d(.+)%s",LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
میشودکد:"015 (christian / 1394"
اگر دقت کنید و بخواهیم دقیق با ترتیب پیش برویم ابتدا که d% آمد یعنی عدد 2 (ابتدای عدد 2015) و بعد هم که +. آمد یعنی هر کاراکتر بعد از آن که از 0 شروع میشود و بعد از آن هم که s% آمد که اولین کاراکتر فضای خالی بعد از آن ، بعد از عدد 5 (بعد از آخرین عدد 2015) هست و طبیعتا مقدار +. یا در واقع مقدار متغییر Content1 برابر 015 میشد فقط اما چرا تا آخرین کاراکتر فضای خالی ، حساب کرد؟ چرا اولین یا حتی این همه کاراکتر فضای خالی بعد از عدد 2 را حساب نکرد؟
علامت یا magic کاراکتر + هر گاه بیاید ، در صورتی که چندین نمونه ی شبیه هم در رشته ی اصلی وجود داشته باشد ، همیشه از اولین تا آخرین نمونه (حداکثر تعداد کاراکتر) را به حساب میاورد ولی دقیق برعکس آن علامت منفی هست که کمترین نمونه را به حساب میاورد (البته این یکی از کارهای علامت - هست و کار مهم منفی را در آینده میآموزیم) یعنی اگر بجای علامت+. علامت -. در پترن بود ، مقدار بازگشتی ، کمترین و کوچک ترین نمونه که همانطور که بررسی کردیم ، کوچک ترین نمونه ی آن در وهله ی اول فقط عدد 015 هست ، برگردانده میشود و در اجرای بارهای بعدی حلقه ، باز هم چک میشود بعد از آن ، این نمونه در ادامه وجود دارد که نتیجه را برمیگرداند (در این مثال برای بار دوم عدد 394 را برمیگرداند) و یا نه
16)
میخواهیم در رشته ی اصلی ، فقط کاراکترهای بین علامت دابل کوتیشن یا " " را استخراج کنیم و همانطور که گفته شد ، برای جستجوی علامت های \ و " در رشته ی اصلی ، در پترنِ آن ، قبل از این علائم ، یک بک اسلش یا \ قرار میدهیم پس در پترنِ این مثال "\ یعنی هر کجا کاراکتر " دیده شد و بعد از آن هم همه ی کاراکترها انتخاب شود (و بعد از آن هم که "\ آمد که باز هم یعنی) تا آخرین کاراکتر " که دیده شد و چون +. در پرانتز قرار گرفتند پس فقط مقادیر این قسمت که محتوای بین دابل وتیشن هستند در متغییر Content1 ذخیره میشود بنابراین محتوای این متغییر رشته ی it's all right میشودکد:s = [[then he said: "it's all right"!]] LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1 = string.find(s, "\"(.+)\"", LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
در خط اول همانطور که میدانید میشود رشته را در دو علامت کلوشه ی باز و بسته [[ ]] تعریف کرد بجای تعریف در دابل کوتیشن " " و چون دابل کوتیشن در خط اول هم جزء رشته بود پس بهتر است داخل کلوشه تعریف شود (یا قبل از دابل کوتیشن ، علامت \ بگذاریم در رشته ی اصلی)
Capture ها (کلوشه) :
کپچرهای پرانتز را بررسی کردیم . حالا به کپچر کلوشه [ ] میپردازیم
تا به حال اگر دقت کرده باشید ، ترتیب پترن هایی که مینوشتیم بسیار بسیار مهم بود مثلا اگر مینوشتیم :
در رشته ی اصلی حتما جایی که عدد (یا مجموع عدد) بیاید و دقیقا بدون هیچ کاراکتر اضافی دیگر ، کاراکتر بعدی آن حروف (یا مجموع حروف) بیاید تا این پترن جواب داشته باشد مثلا "hello 364word" که رشته ی 364word در اینجا پیدا میشود اما اگر رشته به شکل "hello 364 word" چون بعد از عدد ، کاراکتر space که از نوع دیگری است آمد و یا مثلا "hello word365"و یا هر ترتیب دیگر بیاید، دیگر این پترن جوابگو نیستکد:"(%d+%a-)"
کپچرِ کلوشه ، زمانی استفاده میشود که ترتیب پترن ها برای ما مهم نباشد و هر کلاس کاراکتر و یا در واقع هر پترنی را که (داخل کلوشه بود) در هر جایی از رشته ی اصلی جستجو کرد ، در نتیجه و جواب میاورد و اگر آن پترن هم وجود نداشت ، اشکالی ندارد . مثلا اگه 3 کلاس کاراکتر (یا حتی کاراکتری) داخل کلوشه داشتیم ، در رشته ی اصلی ، بدون در نظر گرفتن ترتیب آن ، چه یکی از کلاس کاراکتر اول یا دوم یا سوم و یا حتی هر 3 تای آنها وجود داشته باشد ، نتیجه حاصل میشود و مقدار برگردانده میشود
بنابراین هر چند در کلوشه فقط یک کاراکتر یا کلاس کاراکتر را برای جستجو میشود نوشت ولی زمانی درک میشود که چندین کاراکتر یا کلاس کاراکتر وجود داشته باشد در کلوشه
*** بسیار دقت کنیم که magic کاراکترها یا همان علامت های + و - و * ? (مثلا علامت + که به معنای مجموع کارکتر همان نوع هست) را حتما حتما بعد از کلوشه بیاوریم وگرنه اگر داخل کلوشه بیاوریم ، به معنای جستجوی همان کاراکتر که قبلا مثلا با +% نشان میدادیم هست . کلاس کاراکتر نقطه هم دقیقا مثل همین است یعنی اگر داخل کلوشه بیاید فقط کاراکتر نقطه را جستجو میکند و معنایی که همه نوع کاراکتر را در کلاس کاراکتر ها را برمیگرداند را دیگر ندارد و مثل این است که در جاهای دیگر بنویسیم .% البته بجز کاراکتر ^ که اگر در اولین کاراکترِ بعد از کلوشه بیاید (یعنی داخل کلوشه حتما باید باشد برعکس magic کاراکترها) به معنای نقیض آن پترن یا کلاس کاراکتر هست
*** باز هم دقت کنیم که magic کاراکتر ها (مثلا علامت + و غیره که گفته شد) که یک کاراکترِ بعد از کلوشه بیایند ، همه ی پترن ها و کلاس کاراکترهای بکار رفته شده در کلوشه ی قبلی خود را بصورت مجموع بکار میبرند یعنی مثلا اگر در کلوشه ، عبارت
بیاید و یک کاراکترِ بعد از کلوشه علامت + بیاید ، به این معناست که هم هر کجا کاراکترهای حروف و یا کاراکترهای عدد یافت شد ، صرف نظر از ترتیب آنها ، مجموع این کاراکترها در خروجی ذخیره میشود (علمت پرانتز هم که قبلا کار شد و برای ذخیره کردن آن پترن در متغییر تابع هستکد:"([%a%d]+)"
*** تذکر : در داخل کپچرِ کلوشه ، اگر کلاس کاراکترِ نقطه (که در جاهای دیگر به معنای گرفتن و برگرداندن همه ی کاراکترها بود) بیاید ، فقط به معنای یافتن برای کاراکترِ نقطه هست یعنی دیگر همه ی کاراکترها را برنمیگرداند
17)
همانطور که گفته شد در پترن ، ابتدا و انتها پرانتز آمد یعنی کل جواب یافت شده ، در متغییر Content1 ذخیره شودکد:MainStr = "this year is 2015 + (christian / 1394 solar)" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1 = string.find(MainStr, "([%d+%a-])",LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
بعد از آن کلوشه آمد بدیت معناست که هر چه داخل این کلوشه هست ، ترتیب شان مهم نیست یعنی این طور نیست که چون ابتدا d% آمد ، اگر در رشته ی اصلی عدد ، بعد از یک رشته یا هر کاراکتر دیگری مثل space یا کاراکترهای سجاوندی و ... آمد ، نتیجه ای یافت نشود پس فقط کافی است پترنی را که در کلوشه آمد ، در هر کجای رشته ی اصلی پیدا کند تا آنرا برگرداند یعنی d% که همان عدد هست و یا a% که همان حروف ها هستند را در هر کجای رشته ی اصلی پیدا کند تا آنرا برگرداند
اما نکته ی بسیار حائز اهمیت اینجاست که در پترن ، بعد از d% علامت + آمد و قبلا آموخته بودیم که اگر یک کاراکترِ بعد از کلاس کاراکترها (همان جدولی که در ابتدای آموزش برسی شد که شامل a% و d% و ... بودند) ، magic کاراکتر ها (مثبت و منفی و غیره) بیایند (مثلا فرض کنیم علامت + بیاید) ، مجموع همان نوع کاراکتر (در اینجا باید مجموع کاراکتر های عددی) را برگرداند اما همانطور که گفته شد ، در کلوشه ها ، این مفهوم معنا ندارد و چنین نمیشود حتی مثل همین مثال که بصورت +d% آمد ، مجموع کاراکترهای عددی را برنمیگرداند و انگار مثل حالتی که علامت + را نگذاریم ، فقط هر بار تک تک کاراکترهای عددی را برمیگرداند چرا؟ چون داخل کلوشه ، باید magic کاراکترها بعد از کلوشه بیایند (نه قبل از کلوشه و نه داخل کلوشه) تا مفهومِ مجموع کاراکترهای هم نوع داشته باشند
اما پس در این مثال ، کاراکتر مثبت + که بعد از d% آمد ، اگر به معنای مجموع کاراکترهای عددی نیست ، به چه معناست؟
اگر magic کاراکترها هر جایی جز یک کاراکتر بعد از کلوشه بیایند ، مثلا اگر داخل کلوشه مثل همین مثال بیایند ، فقط به معنای یافتن همان کاراکتر است ( در این مثال علامت + بعد از d% یعنی اگر در رشته ی اصلی ، کاراکتر + وجود داشت ، آنرا پیدا کن) که قبلا با اضافه کردن % قبل از آن کاراکتر ، این کار را انجام میدادیم یعنی در اینجا +d% به این معناست که قبلا مینوشتیم +%d%
همینطور هر چند بعد از a% هم magic کاراکترِ منفی - آمد و معنای خاصی دارد ، اما چون جایی جز یک کاراکترِ بعد از کلوشه آمد (یعنی چون داخل کلوشه آمد) مانند کاراکتر + به معنای آن است که در رشته ی اصلی هر کجا کاراکتر - دیده شد ، برگردانده شود
پس در کل در این پترن ، صرف نظر از ترتیب پترن ها ، بصورت مجزا و تک تک ، به دنبال کاراکترهای مثبت + و منفی - و عدد و حروف میگردد در رشته ی اصلی و حتی اگر وجود هم نداشت ، اشکالی ندارد مثلا چون در رشته ی اصلی کاراکتر منفی - وجود ندارد ، علامت منفی پیدا نمیشود پس اولین تک کاراکتر در رشته ی اصلی که رشته ی t است بررسی میشود که جزء کاراکترهای + یا - یا d% یا a% هست یا نه و چون جزء a% یعنی حروف هست ،در متغییر Content1 برگردانده میشود (گفتیم ترتیب کاراکترها مهم نیست و نه اینکه a% چون در پترن در سومین کاراکتر بود ، پس مثل قبل وقتی سومین کاراکتر این نوع بود باید یافت شود چون داخل کلوشه هست و ترتیب مهم نیست) و همین طور اگر بررسی کنید همه ی کاراکترهای رشته ی اصلی بجز space ها و پرانتز و اسلش ، بقیه بصورت تک تک برگردانده میشود
18)
کل پترن داخل پرانتز هست پس کل نتیجه دی پیدا شده داخل متغییر Content1 ذخیره میشود. همانطور که گفته شد اگر جایی پترن +. میآمد یعنی همه ی کاراکترها برگردانده شود اما چون داخل کلوشه آمدند یعنی فقط کاراکترهای نقطه . و مثبت + جستجو شوند پترن d% هم که عددها را برمیگرداند پس در این مثال هر کجا کاراکترهای نقطه و مثبت و عدد پیدا شدند ، برگردانده میشوند. حالا پترن های موجود در کلوشه تمام میشوند و دقیق کاراکترِ بعد از کلوشه ، magic کاراکتر + هست که این علامت در اینجا به معنای این هست که هر پترن یا کاراکتر یا کلاس کاراکتر (کلا هر چیزی) داخل کلوشه وجود دارد ، بصورت مجموع همان کاراکترها برگردانده شود (دقیق همان علامت + ای که قبلا کار کردیم) اما تفاوت اش این است که شامل همه ی پترن های داخل کلوشه میشود یعنی داخل کلوشه که 3 پترن وجوددارد و d% هست یعنی کلیه ی کاراکترهای عددی که یافت شد را برگرداند و کاراکتر نقطه یعنی هر چند تا کاراکتر نقطه که یافت شد ، پشت سر هم برگرداند و کاراتر + هم همینطور یعنی هر چند تا کاراکتر + که دیده شد ، را با هم برگرداندکد:MainStr = "this year is 2015.11.29 (christian 1394+.....1396 solar)" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1 = string.find(MainStr, "([%d.+]+)",LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
پس در این مثال در ابتدا عدد 2015 دیده شد و برگردانده میشود و در ادامه ی این بازگرداندن ، چون علامت نقطه وجود دارد هم برگردانده میشود (تا حالا شد .2015 برگردانده شد) و در ادامه ی آن نقطه ، عدد 11 هست که برگردانده میشود (تا حالا شد 2015.11 برگردانده شد) و در ادامه هم نقطه و در ادامه ی آن هم عدد 29 برگردانده میشود پس در اولین اجرای حلقه ، رشته ی "2015.11.29" برگردانده میشود
در اجرای دوم هم که مشخص هست عدد 1394 و در ادامه چون کاراکتر + در پترن وجود داشت ، برگردانده و در ادامه هم نقطه های پی در پی (چون علامت + را بعد از کلوشه گذاشتیم ، همانطور که گفته شد ، کاراکترهای مجموع نقطه ها را هم شامل میشد) و در ادامه هم عدد 1396 پس در اجرای بار دوم رشته ی "1396.....+1394" برگردانده میشود
19)
در این پترن ، فقط ابتدای بعد از کاراکتر کلوشه ، کاراکتر ^ قرار داده شد . هر گاه این کاراکتر در اولین کاراکترِ بعد از باز کردن کلوشه بیاید (نه جای دیگر مثلا قبل ی بعد از آن) ، کل کلوشه را نقیض آنرا برمیگرداند یعنی در مثال شماره ی 18 که جواب در دفعه ی اول میشد "2015.11.29" و در دفعه ی دوم میشد "1396.....+1394" این بار نقیض آن یعنی همه ی کاراکترها جز این کاراکترها میشود یعنی جواب در 3 بار تکرار حلقه ، ابتدا رشته ی "this year is " یعنی تا فضای خالیِ قبل از عدد 2 و دفعه ی بعدی حلقه ، رشته یکد:MainStr = "this year is 2015.11.29 (christian 1394+.....1396 solar)" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1 = string.find(MainStr, "([^%d.+]+)",LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
که از کاراکتر فضای خالیِ بعد از عدد 9 و تا کاراکتر فضای خالیِ قبل از عدد 1 هست و سومین بار هم رشته یکد:(christian
یعنی از کاراکتر فضای خالیِ بعد از عدد 6 شروع تا آخر ، برگردانده میشودکد:solar)
20)
همانطور که قبلا کاراکترهایی که مینوشتیم ، باید پی در پی و به ترتیب و از همان نوع میبودند ، هر کاراکتر یا کلاس کاراکتری که خارج از علامت کلوشه باشد ، باید ترتیب مثل قوانین قبل رعایت شود . در این مثال هم چون قبل از کلوشه کاراکتر ^ قرار دارد پس یعنی همین کاراکترِ ^ در رشته جستجو میشود ( شبیه همان ^% ) و حتما اگر در کاراکترِ بعد از آن مواردی که در کلوشه وجود دارد ، در رشته هم موجود بود (که در مثال های بالا بررسی کردیم) و بعد از کلوشه هم که چندین بار اشاره شد که فقط اولین magic کاراکترِ بعد از کلوشه (که در مثال بالا علامت + هست) به معنای مجموع همه ی کاراکترهای هم نوع داخل کلوشه هست (که بررسی کردیم در مثال های قبلی) ولی دومین علامت + بعد از بسته شدن کلوشه ، به معنای جستجو برای کاراکترِ + در رشته ی اصلی هست و چون این کاراکتر در خارج از کلوشه قرار دارد پس بسیار مهم است که اولین کاراکترِ بعد از حاصل داخل کلوشه (که حاصل و نتیجه و مقدار برگشتی داخل کلوشه همان مقدار رشته ی 2015.11.29 هست برای بار اول) ، کاراکتر مثبت در رشته ی اصلی باشد تا جواب حاصل شود پس جواب میشود رشته ی "+2015.11.29^"کد:MainStr = "this year is ^2015.11.29+ (christian 1394+.....1396 solar)" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1 = string.find(MainStr, "(^[%d.+]++)",LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
چون ترتیب کاراکتر ^ قبل از مقدار کلوشه و کاراکترِ + بعد از مقدار کلوشه ، برای حاصل دومین تکرار برای خود کلوشه که رشته ی "1396.....+1394" هست ، وجود ندارد یعنی قبل از رشته ی "1396.....+1394" علامت ^ و بعد از آن علامت + وجود ندارد ، پس این مقدار دیگر برگردانده نمیشود
21)
در این مثال دو تا پرانتز آمد پس محتوای هر پرانتز ، داخل هر متغییر جداگانه (دو متغییر) ذخیره میشود. دو کلوشه ی مجزا آمد یعنی حتما ابتدا باید کلوشه ی اول یافت شود و باز هم حتما دقیق کاراکترِ بعدی آن ، کلوشه ی دوم باید باشد تا نتیجه حاصل شودکد:MainStr = "this *year is 2015.11.29 (christian 1394+1396 solar)" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1, Content2 = string.find(MainStr, "([%d%p])([%a%s]+)",LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1 .."\n"..Content2, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
چون در اولین کلوشه ، آخر آن (یعنی کاراکترِ بعد از بسته شدن اولین کلوشه که سمت چپ هست) هیچ علامتی (magic کاراکتر مثل مثبت و غیره) نیامد ، پس محتوای این کلوشه هر چه که یافت شود ، تک کاراکتری هست (نه مجموعی از آن کاراکتر) ولی چون دو دومین کلوشه ، آخر آن (یعنی کاراکترِ بعد از بسته شدن دومین کلوشه که سمت راست هست) magic کاراکتر مثبت آمد ، پس همه ی پترن ها و محتوای داخل دومین کلوشه ، بصورت مجموع همان نوع کاراکتر یافت میشوند
محتوای کلوشه ی اول d%p% هست یعنی هر کجا که کاراکترهایی از نوع عدد و یا از نوع کاراکترهای سجاوندی (مانند کاراکترهای .^&()[]×~+*/@#$% و از این دست کاراکترها) یا از هر دو نوع ، آن هم بصورت تک کاراکتری دیده شد که دقیق ، کاراکترِ بعد از آن ، محتوای دومین کلوشه که a%s% هست یعنی کاراکترهایی از نوع حروف یا از نوع فضای خالی یا هر دو باشد (بصورت مجموع یا تکی) باشد ، برگردانده شود
اولین جایی که کاراکتر عددی یا سجاوندی دیده میشود ، کاراکتر ستاره در ششمین کاراکترِ رشته ی اصلی هست و دقیق کاراکترِ بعدی آن هم که یا میبایست حروف یا فضای خالی یبود ، کاراکترِ حروفی هست بنابراین تا جایی که کاراکترهای حروف و هم کاراکتر فضای خالی (ترتیب شان فرقی ندارد ولی فقط باید فقط همین دو نوع کاراکتر که داخل کلوشه هست ، باشند) ادامه یابد ، همان رشته در متغییر ذخیره میشوند که در اولین اجرا از کاراکتر y شروع و تا کاراکترِ فضای خالی که قبل از عدد 2 هست ادامه میابند و چون به عدد 2 که رسید ، هیچ کدام از نوع عدد یا space نیست ، پس جواب در اولین اجرای حلقه خاتمه میابد
در اجرای بعدی ، جستجو دوباره از عدد 2 شروع میشود . هر چند عدد 2 از نوع عددی هست (یکی از انواع عددی یا سجاوندی که باید میبود) ولی کاراکترِ بعد از آن که صفر هست ، از نوع حروف یا space نیست و یا هر چند نوزدهمین کاراکتر که نقطه هست ، از نوع سجاوندی هست ولی کاراکتر بعد از آن هم از نوع حروف یا space نیست پس جلوتر میرویم به همین گونه و به عدد 9 که رسیدیم ، کاراکترِ بعد از آن از نوع space هست پس این دو مقدار هم برگردانده میشوند
در سومین اجرای حلقه ، به کاراکتر سجاوندی پرانتز باز برمیخوریم که بعد از آن حروف آمد تا قبل از عدد 1
در چهارمین اجرای حلقه ، بعد از عدد 6 ، کاراکترهای space و حروف آمد تا قبل از کاراکتر پرانتز بسته که حساب میشود
22)
کد:date = "17.492.03 Array.Madule(arr1)^245_54/x-1+Ywq*365Int +space\n\a" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1, CONT = string.find(date, "([%d%.(%s)_])",LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1 .."\n"..CONT, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
این مثال ارور میدهد چون نمیشود از چندین کپچرِ پرانتزِ داخل هم استفاده کرد چون s% هم باز جداگانه داخل یک کپچرِ پرانتزِ دیگر قرار گرفت و کپچرهای تو در تو جواب نمیدهد اما میشود که از متغییر CONT استفاده نکرد یعنی این طور نشود که s% را به عنوان محتوای دیگر حساب کند و داخل متغییر جداگانه ای بریزد
پس در این مثال (در صورت پاک کردن متغییر CONT) ، پرانتزهای کنار کلاس کاراکتر s% به معنای یافتن کاراکتر پرانتز داخل رشته ی اصلی هست
بگذارید برای تفهیم بهتر کلاس کاراکترها که باید بیشتر در همان ایتدا میپرداختیم و زیاد نپرداختیم ، بپردازیم :
23)
برای درک بهتر ، مجبوریم رشته ای با تقریبا تمام کاراکتر بنویسیمکد:date = "+17.492.03 -28 74 ^_ __ __Array25.M*aduleVersion5*( )( Arr1) ^245_54/x-1+Ywq*+365Int +sp*ace^_\n\a" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1 = string.find(date, "(%p+)",LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
هر چند در پترن ، فقط +p% را نوشتیم اما چون نمیشود هر بار بدنه ی مثالی به این بزرگی را با هر کلاس کاراکتر تکرار کرد لذا فقط در توضیحات این مثال ، کلاس کاراکترها را تغییر میدهیم و شما هم طبق توضیحات ، هر بار پترن مورد نظر را تغییر دهید تا نتیجه ی آنرا مشاهده و تجربه کنید
الان که +p% نوشته شد ، یعنی همه ی کاراکترهای سجاوندی که همه ی کاراکترهای علائم دار هستند را برمیگرداند مثل کاراکترهای نقطه و مثبت و غیره .+-*/÷×!@#$%^&*)(_+=?
همانطور که در ابتدای آموزش اشاره شد ، هر کدام از کلاس کاراکترها را اگر با حرف بزرگ آن بنویسیم ، نقیض آن چیزی را که باید پیدا کند ، پیدا میکند یعنی همه ی کاراکترها ، جز آنهایی که بصورت عادی هستند مثلا اگر در مثال بالا ، بنویسیم +P% همه ی کاراکترها بجز کاراکترهای سجاوندی یعنی بجز کاراکترهای .+-*/÷×!@#$%^&*)(_+=? و امثال این کاراکترها ، برگردانده میشود
کلاس کاراکتر نقطه که همه ی کاراکترها را برمیگرداند (این کلاس کاراکتر دیگر نقیض ندارد بلکه فقط میشود محدوده ای برای این کاراکتر مشخص کرد که از کدام کاراکتر تا کدام کاراکتر ، همه ی کاراکتر بین این دو کاراکترِ شروع و پایان را برگرداند)
کلاس کاراکتر a% هم که تمام کاراکترهای حروفی را فقط برمیگرداند (حتما در پترن بالا ، همه ی این کلاس کاراکترها را که توضیح میدهیم ، تست کنید) یعنی بقیه ی کاراکترهای دیگر اعم از عدد و space و ... را شامل نمیشود
کلاس کاراکتر d% همه ی کاراکتر عددی را برمیگرداند (پر واضح هست که D% همه ی کاراکترها بجز عدد را برمیگرداند)
کلاس کاراکتر l% (حرف اِل هست نه حرف یک) همه ی کاراکترِ حروفی (نه بقیه ی انواع کاراکترها) که آنهم فقط با حروف کوچک نوشته شدند را برمیگرداند (پر واضح هست که L% همه ی کاراکترهای حرفی که کوچک نیستند را برمیگرداند)
کلاس کاراکتر u% همه ی کاراکترهای حروفی که با حروف بزرگ نوشته شدند را برمیگرداند (پر واضح هست که L% همه ی کاراکترهای حرفی که بزرگ نیستند را برمیگرداند)
کلاس کاراکتر s% همه ی کاراکترهای فضای خالی یا همان space را برمیگرداند (پر واضح هست که S% همه ی کاراکترهای غیر از space را برمیگرداند)
کلاس کاراکتر مهم w% همه ی کاراکترهای هم حروف و هم عددی را برمیگرداند(حتما این را تست کنید) یعنی کار کلاس کاراکترهای a% و d% را با هم یکجا انجام میدهد (پر واضح هست که W% همه ی کاراکترهای غیر ازحروف و عدد را برمیگرداند)
کلاس کاراکتر x% هم عددهای شانزده دهی (که شامل 0 تا 9 و ABCDEF هستند) را برمیگرداند (X% هم که مشخص هست)
Magic کاراکترهای منفی و ضرب - و * :
مهم ترین و شاید هم اندکی سخت تر و البته بخش جذاب پترن ها ، همین Magic کاراکتر های - و * هست. Magic کاراکتر مثبت که قبلا توضیح داده شد
این Magic کاراکترها (منفی و ضرب) هر چند گاها عملکردی شبیه Magic کاراکتر مثبت دارند اما ماهیت کاملا متفاوتی دارند. Magic کاراکترهای ضرب و منفی ، دو وظیفه را انجام میدهند
Magic کاراکترهای منفی و ضرب ، وظیفه ی الویت بندی پترن کاراکتر یا کلاس کاراکتر (کلا آن قسمت خاص پترن) مربوط به خود را در وهله ی اول دارند بدین معنا که اگر Magic کاراکتر منفی مثلا اگر برای کلاس کاراکتری بیاید ، ابتدا آن کلاس کاراکتر مورد بررسی و جستجو قرار نمیگیرد یعنی ابتدا کلاس کاراکتر یا کاراکتری که الویت برتری دارد (مثلا کاراکتر یا کلاس کاراکتری که Magic کاراکترِ مثبت دارد که الویت برتری دارد ، ابتدا مورد جستجو قرار میگیرد) و حتی اگر آن کلاس کاراکتر مربوط به Magic کاراکتر منفی هم موجود نبود ، مشکلی وجود نخواهد داشت
تذکر مهم : الویت Magic کاراکترها این طور هست که کلاس کاراکتر (منظور a% و ...) یا کاراکتری که اصلا Magic کاراکتر (منظور + و - و *) ندارد باکلاس کاراکتر یا کاراکتری که Magic کاراکترِ مثبت دارد ، هم الویت هستند و بعد از آن ، به ترتیب الویت با Magic کاراکترِ ضرب و بعد از آن هم الویت با Magic کاراکترِ منفی هست یعنی ترتیب الویت ها بصورت + و * و - هست
وظیفه ی دوم این Magic کاراکترهای منفی و ضرب ، شبیه به مثبت هست یعنی مجموع هم نوع آن کلاس کاراکتر یا کاراکتر را برمیگردانند اما تفاوت هایی وجود دارد . Magic کاراکترهای مثبت و ضرب ، بزرگترین جمله (یا نمونه) را برمیگردانند ولی Magic کاراکتر منفی ، کوچک ترین جمله (نمونه) را
مثلا اگر پترنی بنویسیم که در رشته ای ، هر کاراکتری که بین کاراکترهای گیومه " هست را برگرداند ، ولی در رشته ی مورد نظر ، 3 تا از این کاراکترها داشته باشیم ، اگر از Magic کاراکتر مثبت یا ضرب استفاده کنیم ، بین اولین تا آخرین (سومین) کاراکترهای گیومه " هر کاراکتری هست را برمیگرداند اما وقتی از Magic کاراکتر منفی استفاده میکنیم ، بین کوچک ترین جمله (نمونه) که جملات بین دو گیومه ی اول هست (بین گیومه ی اول و دوم) را برمیگرداند و در اجرای بار بعدی حلقه ، جملات و کاراکترهای بین گیومه های بعدی (که گیومه های دوم و سوم) هست را برمیگرداند
برای Magic کاراکترهای هم نوع (که بدیهی هست الویت یکسانی دارند) ، پس از چک کردن بصورت ترتیبی ، اگر هر کدام از آنها موجود نبودند ، اشکالی ندارد
** تذکر بسیار مهم : چون اولین وظیفه ی Magic کاراکترهای منفی و ضرب ، الویت بندی هست (Magic کاراکتر مثبت که همیشه اولین الویت را دارد ، منظورمان نیست) پس فقط یک کاراکتر یا کلاس کاراکتر در پترن ی این Magic کاراکترها را داشته باشند ، مفهوم ندارد و اغلب باعث پیدا نشدن و ارور میشود یعنی مثلا اگر در کل پترنی ، فقط بنویسیم "-d%" چون فقط یک کلاس کاراکتر با Magic کاراکتر منفی آمد ، معنا ندارد . یعنی مثلا باید با یک مجیک کاراکتر دیگر یا بدون کاراکتر بیاید که الویت ها مشخص شود مثلا "-d%p%" بیاید
پس تعریف کامل Magic کاراکترهای مثبت و منفی و ضرب :
مثبت + : اولین و بالاترین الویت را در جستجو در رشته ی اصلی دارد و بزرگترین جمله (نمونه) را برمیگرداند
ضرب * : دومین الویت را در جستجو در رشته ی اصلی دارد و بزرگترین جمله (نمونه) را برمیگرداند
منفی - : سومین الویت را در جستجو در رشته ی اصلی دارد و کوچک ترین جمله (نمونه) را برمیگرداند
تذکرات :
اندکی قلق در Magic کاراکترها هست که بنده به تمام آن پی نبردم ولی تذکراتی که طی تجربه (علمی نیست) میتوانم بدهم این است که :
1) تا میتوانید سعی کنید از قرار دادن Magic کاراکترهای متفاوت در یک پترن ، خودداری کنید (یعنی سعی کنید بیشتر از دو نوع Magic کاراکتر را در یک پترن نیاورید)
2) وقتی از کلاس کاراکترِ نقطه برای یافتن بین دو کاراکترِ خاص استفاده میکنید ، سعی کنید برای این کلاس کاراکترِ نقطه ، Magic کاراکتری که الویت بالاتری نسبت به کاراکتر یا کلاس کاراکترِ اطراف این کلاس کاراکترِ نقطه دارند ، استفاده کنید یعنی برای کلاس کاراکترِ نقطه ، از بالاترین الویت از Magic کاراکترها استفاده کنید
3) سعی کنید چندان از Magic کاراکتر هم نوع ، برای کاراکتر یا کلاس کاراکترهای مختلف استفاده نکنید
24)
خوب در مثال ها دیگر لازم نیست به الویتِ کاراکترها بپردازیم . الان در این مثال در پترن \" که اول و آخرِ پترن آمد ، چون Magic کاراکتری ندارد با قسمت وسط که کلاس کاراکترِ نقطه ، Magic کاراکترِ مثبت دارد (همانطور که گفته شد) الویتی دقیق برابر هم دارند و هر گاه الویت ها برابر بودند ، دقیق مثل حالت قبل ، باید ترتیب کاراکترها و کلاس کاراکترها و کپچرها ، حفظ شوند) پس مثل روال قبل ، باید ترتیب پترن ها رعایت شود یعنی ابتدا کاراکتر گیومه " جتسجو میشود و تا گیومه ی آینده ، هر کاراکتری بین شان برگردانده میشودکد:s = [[then he said: "it's all right" so he got " to the her home" and do see tv!]] LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1 = string.find(s, "\"(.+)\"", LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
اما در کل 3 تا گیومه داخل رشته ی اصلی قرار دارد . یعنی همان نمونه ، بصورت متوالی تکرار شده پس باید از کدام گیومه تا کدام گیومه ، کاراکترها برگردانده شوند؟
اگر Magic کاراکترِ مثبت باشد ، یعنی بزرگترین جمله یعنی از اولین تا آخرین گیومه (نمونه) برگردانده شود (فرضا اگر 1000 تا گیومه وجود داشت ، باز هم همه ی کاراکترهای بین اولین تا آخرین گیومه برگردانده میشدند) که در این مثال ، رشته ی it's all right" so he got " to the her home برگردانده میشود
حالا فرض کنید در پترن ، برای کلاس کاراکترِ نقطه ، بجای Magic کاراکترِ مثبت ، Magic کاراکترِ منفی بود ، چه میشد؟ یعنی اگر پترن این مثال بصورت :
خوب خیلی های شما شاید درست حدس زده باشید اما همانطور که گفته شد ، Magic کاراکترها 2 تا وظیفه دارند که باید هر دوی آنها را حتما چک کنیم)کد:"\"(.-)\""
اولین وظیفه ی Magic کاراکترها ، تعیین الویت برای جستجو بود . برای پترن هایی که Magic کاراکتر ندارند و یا Magic کاراکترِ مثبت دارند (مثل همین چند خط بالا) چک نمیکنیم چون حتما باید ترتیب شان رعایت شود چون بالاترین الویت را دارند ولی برای بقیه ی Magic کاراکترها دانه دانه باید چک شود
چون در اینجا -. از الویت کمتری نسبت به بقیه ی کاراکترهای اطراف خود (منظور پترن های \" در اطراف خود یا هر پترن دیگری) برخوردارند ، پس در ذهن مان آنرا بصورت موقت حذف میکنیم پس چیزی که باقی میماند ، میشود :
حالا این را معنی میکنیم یعنی چه؟ یعنی هر جا گیومه " دیده شد (کاراکتر شماره 15) تا هر جایی که باز هم همین کاراکتر دیده شد (چون از ذهن مان حذف کردیم ، به این معنا ترجمه نکنیم که هر جایی که دو گیومه ی پی در پی دیده شد یعنی اینکه حتما دو تا گیومه باید پی در پی باشند تا حاصل پیدا شود چون -. بین شان وجود داشت) که کاراکترهای شماره ی 27 و 44 هستند. برای اینکه حالا مشخص کنیم کدام گیومه ، دوباره همان کلاس کاراکتر -. را که از ذهن مان حذف کرده بودیم ، برمیگردانیم و مهم ترین نکته اینجاست که اگر این کلاس کاراکتری که در این مثال magic کاراکترِ منفی گرفت ، بین این دو گیومه (یعنی سرجایی که قرار بوده باشد) ، وجود داشت که برگردانده میشود کاراکتراهیی که وجود داشتند ولی اگر وجود هم نداشت ، مشکلی نیست و بجایش یک رشته ی خالی یعنی "" برگردانده میشود چون الویت کمتری دارند (چه منفی یا ضرب که الویت کمتری دارند همینطورند) که منفی به معنای کوچکترین جمله و نمونه بود پس منظورش گیومه ی بعد از گیومه ای که پیدا شد (گیومه ی شماره 2) هست و در دومین تکرار حلقه ، ادامه ی گیومه های بعدی برگردانده میشود (در اینجا گیومه ی 2 تا 3) پس در حلقه ی اول ، رشته ی it's all right" so he got و در حلقه ی تکرار دوم ، رشته ی to the her home برگردانده میشودکد:"\"\""
درست است که این روش ، تفاوتی در نتیجه ی یافتن جواب در این مثال نداشت (یعنی از همان ابتدا مشخص بود که کوچک ترین جمله کدام است و نیاز نبود در ذهن مان چیزی را حذف کنیم) اما در مثال های دیگر اگر به این شیوه بررسی نشود ، درست نیست و به جواب نمیرسیم
25)
کد:date = "17.492.03 Array.Madule(arr1)^245_54/x-1+Ywq*365Int +space\n\a" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1 = string.find(date, "(%d-%a-%s)",LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
در این مثال ، همانطور که گفته شد ، کاراکترهایی که Magic کاراکترِ منفی یا ضرب دارند (در این مثال d% و a%) را در ذهن مان حذف میکنیم پس فقط s% میماند . پس به دنبال کاراکترهای فضای خالی در رشته ی اصلی میگردیم آن هم هر بار فقط یک کاراکترِ فضای خالی (چون s% هیچ Magic کاراکتری ندارد)
اولین کاراکترِ space در کاراکتر شماره ی 10 هست پس این کاراکتر فضای خالی برگردانده میشود (حالا در ذهن مان که حذف کرده بودیم d% و a% را برمیگردانیم) . حالا همانطور که گفته شد ، چون هر دو منفی هستند (هم d% و هم a%) پس هم الویت هستند پس اگر هر دو وجود داشتند یا نداشتند یا یکی از اینها وجود داشتند یا نداشتند ، مهم نیست (هر چند در نکات بیان شد که یک Magic کاراکتر در چند پترن یا کلاس کاراکتر تکرار نشود بهتر هست ولی از این قضیه هم در این مثال استفاده کردیم) و باز هم چون قبل از s% کلاس کاراکتر a% آمد (و هم الویت با d% هست) پس ابتدا این مورد چک میشود و در صورت وجود یا عدم وجود a% ، کلاس کاراکتر d% چک میشود
پس میگردیم ، ببینیم که یک کاراکترِ قبل از این فضای خالی (که در کاراکتر شماره ی 10 بود) ، قبل از آن کوچک ترین نمونه مجموع حروف (مجموع هست چون همه ی Magic کاراکترها این خاصیت را دارند و کوچک ترین نمونه هست چون Magic کاراکترِ منفی هست) آمد یا نه . قبل از این فضای خالی ، عدد 3 آمد که از نوع حروف یا a% نیست پس اشکالی ندارد . پس حالا میچرخیم برای کلاس کاراکتر قبل از آن که d% بود یعنی برای عدد . یعنی میچرخیم ببینیم که کاراکترِ قبل از این فضای خالی (که کاراکترِ شماره ی 10 بود) ، مجموع عددی (کوچک ترین نمونه) آمد یا نه که میبینیم این طور هست و مجموع آن ، همان رشته ی 03 ی قبل از آن است پس در اولین اجرای حلقه ، 03 و بعد از آن ، فقط یک کاراکترِ فضای خالی برگردانده میشود (دقت کنید چون در خروجی ، کارکتر فضای خالی ، در سمت راست هست پس برای ما انگار نامرئی هست که میتوانیم مثلا در حلقه ی دیگری ، تعداد کاراکترهایی که هر بار که این حلقه برمیگرداند را نمایش دهیم یا هر کاراکتر را جداگانه نمایش دهیم تا بهتر درک کنیم) پس در اولین اجرا ، رشته ی :
برگردانده میشود .کد:"03 "
حالا حلقه به اجرای دوم خود میرود و باز همین همین قضیه تکرار میشود یعنی ابتدا در ذهن مان d% و a% را حذف میکنیم پس کاراکتر فضای خالی که در کاراکترِ شماره ی 51 هست ، برگردانده میشود . حالا این a% را که از ذهن مان حذف کرده بودیم را برمیگردانیم ابتدا
پس میبینیم که یک کاراکتر قبل از این کاراکترِ فضای خالی ، آیا مجموع کاراکترهای حروفی وجود دارد؟ بله که میشود رشته ی "Int" و حالا d% را که از ذهن مان حذف کرده بودیم را قبل از این رشته ای که پیدا شد (یعنی قبل از کاراکتر رشته ی "I" که همان کاراکتر شماره ی 48 هست) برای وجودش چک میکنیم یعنی میبینیم که قبل از این کاراکتر شماره ی 48 ، مجموع کاراکترهای عددی وجود دارد؟ که جواب مثبت هست و رشته ی "365" هست . پس در اجرای بار دوم حلقه ، رشته های "365" و "Int" و یک کاراکترِ فضای خالی بعد از آن برگردانده میشود (حواسمان به کاراکتر فضای خالی باشد) یعنی میشود :
در اجرای بار سوم حلقه ، مشخص هست که باز هم برای فضای خالی (کاراکترِ بعد از کاراکتر شماره ی 51) جستجو میشود . باز هم به همین منوال . خود کاراکتر شماره ی 52 ، فضای خالی هست که برگردانده میشود . در قدم بعدی ، برای مجموع حروف a% قبل از این کاراکتر (یعنی قبل از کاراکترِ شماره ی 52 که میشود 51) جستجو میشود که کاراکتر شماره 51 چون فضای خالی هست و حروف نیست پس a% برای آن بی نتیجه هست . بنابراین برای مجموع عدد a% در قبل از کاراکتر space (یعنی قبل از کاراکترِ شماره ی 52 که میشود 51) جستجو میشود که همانطور که میدانید ، کاراکتر شماره 51 چون فضای خالی هست و عدد نیست پس d% برای آن بی نتیجه هست پس هم d% و هم a% برای آن یافت نمیشود که اشکالی ندارد چون Magic کاراکتر با الویت پایین داشتند . پس در اجرای بار سوم ، فقط همان تک کاراکترِ فضای خالی که ابتدا برگردانده شده بود ، برگردانده میشودکد:"365Int "
در اجرای حلقه در دفعات بعدی (تا اجرای بار هفتم) هم همینطور فقط هر بار تک کاراکترهای فضای خالی (که بین کاراکترهای شماره ی 51 تا 58 هستند) برگردانده میشود
در اجرای بار هشتم حلقه ، عرض شود که در این مثال ، رشته ی "n\" در رشته ی اصلی را به عنوان فضای خالی شناسایی میکند (چرا را دقیقا نمیدانم) پس با همان روال ، رشته ی :
برگردانده میشودکد:"space "
26)
خوب مشخص هست که این پترن ، تمام رشته ها را انتخاب میکند یعنی در اجرای بار اول ، رشته ی :کد:date = "17.492.03 ^_ __ __Array25.M*aduleVersion5*( )( Arr1) ^245_54/x-1+Ywq*+365Int +sp*ace^_\n\a" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1 = string.find(date, "(.*)",LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
برگردانده میشود (بجای n\ به خط بعدی میرود و بجای a\ چیزی شبیه ستاره چاپ میشود که بدیهی است)کد:"17.492.03 ^_ __ __Array25.M*aduleVersion5*( )( Arr1) ^245_54/x-1+Ywq*+365Int +sp*ace^ *"
اما طبق نکاتی که برای Magic کاراکترها گفته شد ، در اجرای دفعه ی بعدی با آنکه تمام مقادیر برگردانده شدند و کاراکتر دیگری در رشته ی اصلی وجود ندارد ، چون یک Magic کاراکتر با الویت کمتر (ضرب) را فقط برای یک کلاس کاراکتر آوردیم و کلاس کاراکتر دیگری با الویت متفاوت نیاوردیم ، در دفعات بعدی هم اجرا میشود (حلقه ی بی نهایت) که این بار برخلاف معمول ، متغییر LastChar یک واحد از متغییر FirstChar کوچکتر میشود (در دفعات حلقه ی بعد برای همیشه ) و متغیر Content1 هم تا آخر رشته ی خالی را برمیگرداند که همانطور که گفته شد یا باید از Magic کاراکترهای متفاوت در پترن استفاده کنیم یا شرط حلقه (if داخلی) را به صورت زیر تغییر دهیم :
کد:date = "17.492.03 ^_ __ __Array25.M*aduleVersion5*( )( Arr1) ^245_54/x-1+Ywq*+365Int +sp*ace^_\n\a" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1 = string.find(date, "(.*)",LastChar+1) if Content1==nil or (LastChar<FirstChar and Content1=="") then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
27)
این یکی از مثال های مهم هست (ولی نه اینکه سخت باشد)کد:date = "17.492.03 ^_A38rray25.MaduleVersion5*(arr1) ^245_54/x-1+Ywq*+365Int +space^_\n\a" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1 = string.find(date, "([%p%s]*[%w]*)",LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
میدانیم که Magic کاراکترها (مثبت و ضرب و منفی) برای کپچرهای کلوشه ، یک کاراکتر بعد از بسته شدن کلوشه میآیند واگر کاراکترهای + و * و - داخل یا قبل یا هر کجای دیگر جز جایی که گفته شد بیایند ، به عنوان کاراکتری برای جستجو در رشته ی اصلی در نظر گرفته میشوند
پس در پترن این مثال ، هر دو کلوشه ، Magic کاراکتر ضرب را دارند و چون Magic کاراکتر یکسانی دارند ، همانگونه که گفته شد ، به ترتیب چک میشوند یعنی ایتدا داخل کپچر کلوشه اول چک میشد و اگر وجود نداشت ، داخل عبارت بعدی آن یعنی داخل کلوشه ی بعدی چک میشود و ...
از قبل میدانیم ، داخل یک کلوشه ، پترنی نوشته شود ، ترتیب آن مهم نیست
پس ایتدا ، داخل کلوشه ی اول که s%p% هست ، در کاراکترِ اول در رشته ی اصلی جستجو میشود بدین معنا که کاراکتر اولِ رشته ی اصلی که 1 هست ، آیا از نوع فضای خالی هست؟ نه . از نوع کاراکترهای سجاوندی چطور؟ نه
پس برای کلوشه ی اول ، جوابی یافت نشد ولی چون الویت های دو کلوشه در یک سطح هست ، پس جواب ندادن یکی ، باعث میشود کلوشه یا همان عبارت بعدی جستجو شود (اما همانطور که گفته شد ، اگر در کل پترنی ، فقط عبارت هایی که با Magic کاراکتر های یکسان بکار برده شد ، باید حداقل برای یکی از آنها ، مقداری یافت شود یعنی نمیشود هیچ مقداری برای هیچ کدام یافت نشود) لذا در کارکتر اول رشته ی اصلی ، این بار (نه اینکه دو اجرای بعدی حلقه باشیم) کلوشه ی دوم که w% هست چک میشود. آیا کاراکتر اول که عدد 1 هست ، از نوع w% که همان حرف یا عدد هست ، هست؟ بله
پس چون Magic کاراکتر ضرب برای این کلوشه وجود دارد ، مجموع عبارت و کاراکترهای عدد و حروف را که در اجرای بار اول همان رشته ی "17" میشود ، برگردانده میشود
برای دفعات بعدی تکرار حلقه هم دقیقا به همین روش. بار دوم کاراکتر سوم در رشته ی اصلی که نقطه هست که از نوع یافت شده در کلوشه ی اول که همان p% هست پیدا شد . حالا این بار چون پیدا شد ، برای کلوشه ی دوم ، بک کاراکتر بعد از این کاراکتر (یک کاراکتر بعد از کاراکتر نقطه که همان کاراکتر شماره ی 4 که عدد 4 است) جستجو میشود یعنی جستجو میشود که عدد 4 ، جزء w% هست؟ بله پس کل نوع عدد و حروف (w%) انتخاب میشود که رشته ی "492" میشود . پس برای بار دوم برای کلوشه دوم که رشته ی نقطه بود و در ادامه ی آن رشته ی 492 برگردانده میشود که کلا میشود رشته ی "492."
بار سوم هم به همین ترتیب رشته "03."
بار چهارم باز هم کلوشه ی اول برای کاراکتر شماره 10 که فضای خالی هست جستجو میشود . چون جزء s% هست پس انتخاب میشود و کاراکترهای بعدی آن هم چون جزء کاراکترهای سجاوندی (در کلوشه ی اول) هستند ، انتخاب میشوند و کاراکتر بعد از آن هم که رشته ی "A" هست ، کلوشه ی دوم برای آن جستجو میشود. پس تا رشته ی "A38rray25" انتخاب میشود پس برای بار چهارم رشته ی :
برگردانده میشودکد:"^_A38rray25"
به همین ترتیب بار پنجم :
بار ششم :کد:".MaduleVersion5"
هفتم :کد:"*(arr1"
هشتم :کد:") ^245"
نهم :کد:"_54"
دهم :کد:"/x"
یازدهم :کد:"-1"
دوازدهم :کد:"+Ywq"
سیزدهم :کد:"*+365Int"
چهاردهم :کد:" +space"
کد:"^_"
اما نکته ی مهمی که در این مثال نهفته هست و قبلا هم کار شده ، تکرار حلقه برای بار پانزدهم و از آن به بعد هست . چون در کل پترن ، از Magic کاراکترهای هم نوع و آنهم با الویت کم (ضرب یا منفی) استفاده شده پس مثل مثال قبل ، حتی اگر حلقه هم تا رشته ی آخر آن یافت شود ، باز ادامه میباید تا حلقه ی بی نهایت و همانطور که گفته شد ، شرط if داخل حلقه را باید درست کرد :
کد:date = "17.492.03 ^_A38rray25.MaduleVersion5*(arr1) ^245_54/x-1+Ywq*+365Int +space^_\n\a" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1 = string.find(date, "([%p%s]*[%w]*)",LastChar+1) if Content1==nil or (LastChar<FirstChar and Content1=="") then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
28)
این مثال مثل مثال قبل هست فقط در پترن ، برای کلوشه ی دوم ، بجای Magic کاراکترِ ضرب ، Magic کاراکترِ منفی قرار دادیمکد:date = "17.492.03 ^_A38rray25.MaduleVersion5*(arr1) ^245_54/x-1+Ywq*+365Int +space^_\n\a" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1 = string.find(date, "([%p%s]*[%w]-)",LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
خوب در این مثال چون Magic کاراکترِ ضرب بر Magic کاراکترِ منفی الویت دارد پس کلوشه ی دوم را از ذهن مان حذف میکنیم و کلوشه ی اول ، باید محتوایی برایش یافت شود اما اگر محتوایی برای اولین کلوشه یافت نشود ، چون کل پترن ، فقط از Magic کاراکترهای با الویت پایین (منفی و ضرب) استفاده شد ، در حلقه ی بی نهایت گیر میکند لذا کلوشه ی اول که s%p% هست ، در اولین کاراکتر رشته ی اصلی که 1 هست ، جستجو میشود و چون 1 کاراکتری از جنس s% و p% نیست پس حلقه در بی نهایت گیر میکند که برای رفع این مشکل ، یا باید شرط if داخل حلقه را مثل قبل درست کنیم یعنی بجایش بنویسیم :
یا اینکه در پترن ، از هیچ Magic کاراکتری استفاده نکیم که در این صورت ، کاراکتر مورد نظر ، الویتی برابر با Magic کاراکتر مثبت دارد یا اینکه از Magic کاراکتر مثبت استفاده کنیم مثلا پترن را این جوری بنویسیم که w% آخری هیچ Magic کاراکتی ندارد :کد:if Content1==nil or (LastChar<FirstChar and Content1=="") then
یا Magic کاراکترِ مثبت دارد :کد:"([%p%s]*[%w]-%d)"
که در هر صورت معنا و هدف پترن عوض میشود پس این مهم است که از ابتدا ، روی الگوریتم درست در پترن ها کار کنیمکد:"([%p%s]*[%w]-%d+)"
در همین مثال ، شماره ی 28 ، اگر در رشته ی اصلی همان ابتدا و به عنوان اولین کاراکتر ، یکی از کاراکتر از نوع هایی که در کلوشه ی اول بود را اضافه میکردیم (مثلا مینوشتیم "17_" یعنی یک آندرلاین همان ابتدا اضافه میکردیم) مشخص است که چون یکی از نوع های داخل کلوشه (از نوع p%) بود ، پس در اولین بار اجرای حلقه ، رشته ی "17_" برگردانده میشد ولی در اجرای دوم به بعد حلقه ، مثل حالت بالا که توضیح داده شده ، در حلقه ی بی نهایت گیر میکرد
*** پس این قضیه بسیار مهم است که همان ابتدا چک کنید ببینید در پترن تان آیا همه ی کاراکترهای تان ، Magic کاراکترهایی با الویت پایین دارند یا خیر یعنی اینکه چک کنید و ببینید همه ی کاراکترهایتان (اعم از کلاس کاراکتر و کپچرها و کاراکترهای معمولی و غیره) ، آیا Magic کاراکترِ منفی یا ضرب را فقط دارند یا نه؟ اگر جواب مثبت باشد پس با دستورات شرط عادی ، حلقه در بی نهایت گیر میکند
29)
این مثال مثل مثال قبل هست فقط در پترن ، برای کلوشه ی دوم ، بجای Magic کاراکترِ منفی، Magic کاراکترِ مثبت قرار دادیمکد:date = "17.492.03 ^_A38rray25.MaduleVersion5*(arr1) ^245_54/x-1+Ywq*+365Int +space^_\n\a" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1 = string.find(date, "([%p%s]*[%w]+)",LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
کلوشه ی اول ضرب و کلوشه ی دوم مثبت است . همانطور که میدانیم ، Magic کاراکترِ مثبت ، الویت بیشتری دارد پس در ذهن مان کلوشه ی اول را حذف میکنیم. پس نوع کلوشه ی دوم که w% یعنی حروف و عدد هست را میبینیم در اولین کاراکترِ رشته ی اصلی آمد یا نه؟
بله آمد پس چون Magic کاراکتر مثبت دارد ، همه و مجموع آن عدد را که میشود "17" انتخاب میشود. حالا کلوشه ی اول را که قبل از کلوشه ی دوم بود را دوباره در ذهن مان میاوریم . چون کلوشه ی اول ، قبل از کلوشه ی دوم هست ، پس یک کاراکتر قبل از اولین کاراکترِ محتوای کلوشه ی دوم (که 1 هست) را برای s%p% جستجو میکنیم و هیچ نتیجه ای یافت نمیشود (چون قبل از اولین کاراکتر ، کاراکترِ دیگری وجود ندارد) و چون ضرب ، الویت کمتری دارد نسبت به مثبت ، پس پیدا نشدن نتیجه ، هیچ اشکالی ندارد پس در اولین اجرای حلقه ، رشته ی "17" برگردانده میشود
در دومین اجرای حلقه هم همینطور. از سومین کاراکتر و از آن به بعد در رشته ی اصلی، دنبال حروف یا عدد که همان w% یا کلوشه ی دوم هست ، جستجو میشود که نتیجه کاراکتر چهارم تا تا ششم که "492" هست میشود . حالا قبل از اولین کاراکترش یعنی قبل از عدد 4 ، دنبال محتوای اولین کلوشه که s%p% بود جستجو میشود که قبل از آن ، نقطه که جزء کاراکترهای سجاوندی هست ، یافت میشود پس نتیجه رشته ی "492." میشود
در سومین اجرا هم به همین گونه رشته ی "03." برگردانده میشود
در چهارمین اجرا ، باز هم ابتدا دنبال کلوشه ی دوم که حروف یا عدد هست پس رشته ی "A38rray25" پیدا میشود و قبل از "A" در این رشته هم دنبال محتوای کلوشه ی اول گشته میشود و اگر وجود داشت ، لحاظ میشود پس یک space و کاراکترهای ^ و _ قبل از آن برگردانده میشود که کلا میشود رشته ی :
پنجمین اجرا :کد:^_A38rray25
که اغلب شبیه مثال شماره ی 27 هستکد:".MaduleVersion5"
نکته ی این مثال اینجاست که چون در این مثال از Magic کاراکتر با بزرگترین الویت یعنی Magic کاراکتر مثبت استفاده شد ، پس حلقه در بی نهایت گیر نمیکند (حتی اگر برای آن مقداری یافت نشود که در آن صورت جوابی پیدا نمیشود نه اینکه مثل قبلی ، در حلقه ی بی نهایت گیر کند)
30)
یکی از مهم ترین مثال هاستکد:MainStr = "this year is 2015.11.29 (christian 1394 solar)" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1, Content2, Content3 = string.find(MainStr, "([^%d.+]+)(.-)(%d*)",LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1 .."\n"..Content2 .."\n"..Content3, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
پرانتز اول که همان کلوشه هم در آن هست ، magic کاراکترِ مثبت آمده که مشخص هست نسبت به پرانتز دوم که magic کاراکتر منفی و سومی که magic کاراکتر ضرب آمد ، الویت دارد پس در ذهن مان پرانتزهای دوم و سوم را حذف میکنیم
همانطور که در چندین مثال قبل بررسی شد ، جواب پترن :
در 2 بار اجرا ، به ترتیب رشته های "2015.11.29" و "1394" میشدکد:([%d.+]+)
اگر دقت کنید ، اول کلوشه علامت ^ آمد که گفتیم اگر در اینجا بیاید ، به معنای معکوس آن عبارت هست که در اینجا یعنی همه ی کاراکترها بجز رشته های "2015.11.29" و "1394" پس میشود 3 بار یعنی 3 رشته ی :
وکد:"this year is "
وکد:" (christian "
با احتساب فضای خالی آن که باید دقت کنیدکد:" solar)"
پس برای اجرای اولین بارِ حلقه رشته ی
برگردانده میشود آنهم فقط جواب قسمت داخل کلوشه یا پرانتز اول . خوب حالا جواب پرانتز دوم و سوم میماند . چون باز Magic کاراکترِ ضرب ، از Magic کاراکترِ منفی بیشتر الویت دارد پس به سراغ پرانتز سومی میرویم و دومی را در ذهن مان حذف میکنیم یعنی تصویر میکنیم که این طور نوشته شده پترن مان :کد:"this year is "
یعنی بعد از جواب آخرین کاراکتری که در پرانتز اول که همان آخریم کاراکترش space بود ، از این کاراکتر به بعد ، دنبال عدد بگرد. وقتی از این کاراکتر space (کاراکتر شماره ی 13) به بعد دنبال عدد میگردیم ، 2 تا مجموع عدد پیدا میکنیم که یکی جوابش رشته ی "2015" میشود و دیگری (آخری) رشته ی "1394"کد:"([^%d.+]+)(%d*)"
اما کدام یک از این اعداد جواب سومین پرانتز مان هست؟
جواب این سئوال در Magic کاراکترِ (علامت) عبارت قبلی هست یعنی چون عبارت یا پرانتز قبلی که Magic کاراکتر اش منفی بود ، یعنی کوتاه ترین جمله و نزدیک ترین جمله به جواب عبارت پرانتز اول را بیای نه بزرگترین را پس جواب پرانتز سوم یعنی d% رشته ی "2015" میشود
خوب جواب سومین پرانتز که رشته "2015" میشود و دقیق از کاراکتر قبل از آن (space) هم که جواب پرانتز یا همان کلوشه ی اولی هست پس در اینجا دومی که (-.) بود جوابش چه میشود؟ پس جوابی برایش پیدا نشد و چون Magic کاراکتر منفی دارد ، اگر هم جوابی برایش پیدا نشود ، ایرادی ندارد بنابراین در خروجی بجای پرانتز دومی رشته ی "" چاپ میشود (دقت کنید که این رشته حاوی space یا فضای خالی نیست هر چند در اجرا شبیه فضای خالی چاپ میشود
پس برای پرانتز اول یا متغییر Content1 ، رشته ی :
و برای پرانتز دوم یا متغییر Content2 رشته ی :کد:"this year is "
و برای پرانتز سوم یا متغییر Content3 رشته ی :کد:""
برگردانده میشود.کد:"2015"
در اجرای بار دوم حلقه هم بعد از کاراکتر "29" جستجو میشود که اگر همین ترتیب را رعایت کنید ، برای پرانتز اول ، رشته ی :
و برای پرانتز دوم رشته ی :کد:" (christian "
و برای پرانتز سوم رشته ی :کد:""
برگردانده میشودکد:"1394"
در سومین اجرای حلقه ، و برای پرانتز اول رشته ی :
و برای دو تا پرانتز دیگر ، مقداری یافت نمیشود و چون الویت کمتری نسبت به پرانتز اول دارند ، پیدا شدن یا نشدن جواب در نتیجه ی پرانتز اول تاثیری ندارد پس مقدار پرانتز دوم و سوم هم میشود :کد:" solar)"
کد:""
تذکر مهم : بر اساس تجربه ، سعی کنید اگر از کلاس کاراکترِ نقطه برای انتخاب تمامی یا بخشی از کاراکترهای بین دو علامت خاص که استفاده میکنید ، علامتِ Magic کاراکتر را برای کلاس کاراکترِ نقطه ، مثبت در نظر بگیرید نه منفی یا ضرب یعنی (+.)
31)
همان مثال قبلی هست فقط Magic کاراکترهای پرانتز دوم و سوم تغغیر کردکد:MainStr = "this year is 2015.11.29 (christian 1394 solar)" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1, Content2, Content3 = string.find(MainStr, "([^%d.+]+)(.*)(%d+)",LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1 .."\n"..Content2 .."\n"..Content3, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
جواب پرانتز اول که مشخص بود میشود رشته ی :
Magic کاراکترِ پرانتز سوم چون باز الویت دارد ، این پرانتز محتویاتش جستجو میشود یعنی بعد از کاراکتر شماره ی 13 که فضای خالی هست ، برای پرانتز سوم که d% هست یعنی برای عدد جستجو میشود که مثل قبل ، دو عدد "2015" و "1394" یافت میشودکد:"this year is "
اینکه کدام عدد انتخاب شود ، بستگی یه Magic کاراکتر پرانتز قبلی یا پرانتز دومی دارد که ضرب هست یعنی بزرگترین و دورترین جمله و نمونه انتخاب شود که دورترین عدد نسبت به جواب و رشته ی پرانتز اول ، عدد و رشته ی "1394" هست تا کاراکتر های مابین شان (یعنی کاراکترهای ما بین پرانتز اول که رشته ی "this year is " بود و رشته ی "1394") همان جواب رشته ی دوم شود پس جواب ها به این صورت میشود :
پرانتز اول یا مقدار متغییر Content1 :
پرانتز دوم یا مقدار متغییر Content2 :کد:"this year is "
پرانتز سوم یا مقدار متغییر Content3 :کد:"2015.11.29 (christian 139"
تذکر : اینکه چرا جواب پرانتز سوم یا همان متغییر Content3 ، بجای اینکه کل رشته ی "1394" شود (چون علامت Magic کاراکتر مثبت دارد که یعنی مجموع کاراکتر آن نوع که عدد هست) فقط تک کاراکترِ و رشته ی "4" شد ، دلیل اش را نمیدانمکد:"4"
تمرین (بدون توضیح)
32)
در حل این مثال ، به نکته ای که در خط آخر مثال شماره ی 30 گفته شد ، دقت کنیدکد:MainStr = "this year is 2015.11.29 (christian 1394 solar)" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1, Content2 = string.find(MainStr, "(.-)(%d-)",LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1 .."\n"..Content2, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
تذکر مهم : بر اساس تجربه ، سعی کنید اگر از کلاس کاراکترِ نقطه برای انتخاب تمامی یا بخشی از کاراکترهای بین دو علامت خاص که استفاده میکنید ، علامتِ Magic کاراکتر را برای کلاس کاراکترِ نقطه ، مثبت در نظر بگیرید نه منفی یا ضرب یعنی (+.)
تمرین (بدون توضیح)
33)
کد:date = "17.492.03 ^Array.MaduleVersion5*(arr1) ^245_54/x-1+Ywq*+365Int +space^_\n\a" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1 = string.find(date, "(.-)",LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
Magic کاراکترِ علامت سئوال (علامت سئوال انگلیسی) ? :
آخرین Magic کاراکتر علامت سئوال هست که ماهیت اش کلا با بقیه فرق دارد . اگر Magic کاراکترِ علامت سئوال بعد از یک کاراکتر یا کلاس کاراکتر یا کپچر کلوشه ای یا هر چیز دیگری بیاید ، اولا دیگر نمیشود از Magic کاراکتر دیگری مثلا مثبت و منفی و ضرب استفاده کرد پس کاراکتری که Magic کاراکترِ علامت سئوال میپذیرد ، فقط یک کاراکتر از همان نوع (مثلا عدد) را میتواند برگرداند نه مجموع آنرا و دوما بدین معناست که کاراکتر مورد نظر ، اگر وجود داشته باشد ، گذاشته شود وگرنه وجود هم نداشت ، مهم نیست (مثل الویت بندی Magic کاراکترها مثلا مثل Magic کاراکترِ منفی که پایین ترین الویت را داشت اما فرقش با Magic کاراکترِ منفی در این است که Magic کاراکترِ منفی ، مجموع کاراکتر همان نوع را برمیگرداند ولی Magic کاراکترِ علامت سئوال تنها یک کاراکتر را برمیگرداند)
34)
همانطور که میدانیم ، Magic کاراکترِ علامت سئوال ، کمترین الویت را دارد پس در اجرای هر بار از حلقه ، a% را از ذهن مان پاک میکنیم بنابراین فقط به دنبال مجموع عدد میگردیم و بعد از یافتن عدد ، اگر فقط یک کاراکترِ قبل از این عدد (چون Magic کاراکترِ علامت سئوال فقط تک کاراکتر را برمیگرداند) از نوع حروف بود ، آن هم در جواب گنجانده میشود و اگر نبود هم که مهم نیست پس در بار ال ، رشته ی "17" برگردانده میشود و چون قبل از عدد 1 از آن ، کاراکتری وجود ندارد که بخواهد از نوع حرف باشد یا نه ، پس در دفعه ی اول کلا فقط همین رشته برگردانده میشودکد:date = "+17.492.03 -28 74 ^_ __ __Array25.M*aduleVersion5*( )( Arr1) ^245_54/x-1+Ywq*+365Int +sp*ace^_\n\a" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1 = string.find(date, "(%a?%d+)",LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
در دفعه ی دوم به بعد هم به ترتیب رشته های 492 و 03 و 28 و 74
در بار ششم ، عدد 25 یافت میشود (بعد از رشته ی "Array") و چون کاراکتر قبل از کاراکتر اول آن (قبل از عدد "2") ، کاراکتری از نوع حروف وجود دارد پس فقط همین یک کاراکترِ قبل از آنرا برمیگرداند که کلا رشته ی "y25" برگردانده میشود
بار هفتم هم به همین ترتیب رشته ی "n5" برگردانده میشود
و میتوانید تا آخر جواب ها را همینطور چک کنید ...
35)
یکی از کاربردهای Magic کاراکترِ علامت سئوال ، در قضیه ی یافتن علائم قبل از عدد مثل مثبت یا منفی بودن عدد هست :کد:date = "+17.492.03 -28 74 ^_ __ __Array25.M*aduleVersion5*( )( Arr1) ^245_54/x-1+Ywq*+365Int +sp*ace^_\n\a" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1 = string.find(date, "([+-]?%d+)",LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
باز هم مشخص است که در این پترن اگر قبل از عددی ، علامت های مثبت یا منفی در رشته ی اصلی بیایند ، به همراه عدد ، آن علائم را برمیگرداند و اگر هم که نیامده باشد ، فقط عدد را برمیگرداند
پس در اجرای بار اول ، رشته ی "17+" و بار دوم به بعد رشته های 492 و 03 و 28- و .... که آخری هم 365+ را برمیگرداند
این مثال با کد زیر هم تقریبا برابری میکند (بجای علامت سئوال از منفی استفاده شد که البته فرق شان را میدانیم) :
36)
کد:date = "+17.492.03 -28 74 ^_ __ __Array25.M*aduleVersion5*( )( Arr1) ^245_54/x-1+Ywq*+365Int +sp*ace^_\n\a" LastChar = 0 var=false while var==false do FirstChar, LastChar, Content1 = string.find(date, "([+-]-%d+)",LastChar+1) if Content1==nil then var=true break; else Dialog.Message("Notice", FirstChar.."\n"..LastChar.."\n"..Content1, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end end
نکته جدید و مهم :
اگر تابع string.find را در شرطی بکار ببریم که با علامت ^ پترن آن شروع شود ، آن نوع پترن را در ابتدای رشته ی اصلی ، چک میکند مثلا :
37)
کد:date = "17.492.03 -28 74 ^_ __ __Array25.M*aduleVersion5*( )( Arr1) ^245_54/x-1+Ywq*+365Int +sp*ace^_\n\a" if string.find(date, "^%d") then Dialog.Message("Notice", "string begining with digit", MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); else Dialog.Message("Notice", "string Dont begining with digit", MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end
تابع string.gsub :
دقت کنید این تابع را با تابع string.sub که برای بریدن رشته بود و توضیح داده شده بود ، اشتباه نگیرید. این تابع برای جایگزین کردن یه رشته بجای رشته یا پترن مورد نظر در رشته ی اصلی بکار میرود
دارای 4 آرگومان و 2 خروجی برای تابع هست
در آرگومان اول ، مثل تابع string.find رشته ی اصلی که برای جستجو برای جایگزین کردن را میدهیم
در دومین آرگومان هم باز هم مثل همان تابع ، رشته ای به عنوان پترن یا حتی رشته ی معمولی ای میدهیم تا اگر این رشته در رشته ی اصلی (آرگومان شماره اول) جستجو و یافت شد ، رشته ای که در آرگومان شماره ی سوم دادیم را جایگزین آن کند
در سومین آرگومان ، رشته و حتی رشته ای به عنوان پترن ای میدهیم که اگر مقدار رشته ی آرگومان دوم در رشته ی آرگومان اول (اصلی) یافت شد ، این رشته ی شماره ی 3 را جایگزین رشته ی شماره ی 2 در رشته ی اصلی (آرگومان اول) کند (در این آرگومان حتی بجای رشته میشود تابع هم نوشت که میتوانید برای آموزش نوشتن تابع در این آرگومان ، به سایت اصلی لوا سر بزنید)
چهارمین آرگومان که اختیاری است ، یک عددی را میدهیم که فقط به تعداد آن اگر مقداری یافت شد ، جایگزین شود نه به تعداد همه ی مقادیری که این تابع یافت کرد یا به اصطلاح Match کرد (به مثال 39 دقت کنید ، متوجه منظور میشوید)
این تابع اگر مقداری را که در آرگومان دوم به عنوان رشته ی پترن یا رشته ی معمولی دادیم را در رشته ی اصلی (رشته ای که در آرگومان اول دادیم) را پیدا کرد ، 2 خروجی را برمیگرداند. اولین خروجی تابع (متغییر تابع) ، رشته ی اصلاح شده و جایگزین شده (رشته ی سوم که بجای رشته ی دوم در رشته ی اول جایگزین شد) را برمیگرداند و دومین خروجی تابع ، در صورتی که آرگومان چهارمی داده شود آنرا برمیگرداند وگرنه تعداد مواردی که یافت شد (تعداد مواردی که آرگومان دوم در آرگومان اول وجود داشت) یا به اصطلاح تعداد Match ها را برمیگرداند و اگر هم مقداری یافت نشود ، در اولین متغییر خروجی ، nil برگردانده میشود
*** تذکر مهم : در تابع string.gmatch مثل تابع string.find نیست که هر بار برای یافتن مقدار بعدی ، آنرا در حلقه ای قرار دهیم که شماره ی کاراکترِ آنرا در آرگومان سوم تابع string.find میدادیم . تابع string.gmatch اتوماتیک رشته ی آرگومان دوم را در همه ی کاراکترهای رشته ی آرگومان اول جستجو میکند یعنی بدون استفاده از حلقه ، کار حلقه و آرگومان سوم تابع string.find را انجام میدهد و نتیجه را برمیگرداند
38)
در خط دوم ، پترن (در آرگومان دوم) را در رشته ی اصلی (آرگومان اول) جستجو میکند و هر گاه مقداری یافت ، آرگومان شماره سوم را جایگزین آن میکندکد:MainStr = "Today is 2015/12/3, firm" ModifiedStr, MatchNumber = string.gsub(MainStr, "%a+", "word\n") Dialog.Message("Notice", ModifiedStr.."\n"..MatchNumber , MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
خوب پترن +a% که مشخص هست . اولین مقداری که پیدا میکند رشته ی "Today" در رشته ی اصلی هست (به مقادیری که پیدا میشود ، Match هم میگویند یعنی الان رشته ی "Today" اولین match ماست). خوب همانطور که گفته شد ، تابع string.gmatch مثل تابع string.find نیست که برای پیدا کردن match ها یا مقادیری که در پترن داده شد ، در حلقه ای قرار دهیم برای اینکه تا آخرین کاراکترِ رشته ی اصلی ، برای جستجو چک کند. تابع string.gmatch اتوماتیک خودش همه ی کاراکترهای رشته ی اصلی را برای جستجو چک میکند پس برای جستجو به سراغ کاراکتر بعدی که space و از آن به بعد هست میرود . بنابراین دومین مقدار (دومین Match) یافت شده ، رشته ی "is" در رشته ی اصلی است و به همین ترتیب ، سومین match آن هم رشته ی "firm" میشود
خوب حالا کار این تابع این هست که سومین آرگومان که رشته ی "word\n" هست را جایگزین این مقادیر یا match هایی که پیدا شد ، قرار میدهد یعنی بجای رشته های "Today" و "is" و "firm" در رشته ی اصلی ، رشته ی "word\n" را قرار میدهد (میدانیم که هر کجا "\n" آمد ، این مقدار چاپ نمیشود یعنی به خط پایین تر میورد) پس ابتدا رشته ی "word" چاپ میشود و به خط بعدی میرود و سپس کاراکتر space چاپ میشود (همان کاراکتری که بین رشته های "Today" و "is" بود) و سپس باز هم رشته ی "word" چاپ میشود و به خط بعدی میرود و سپس دوباره کاراکتر space چاپ میشود (همان کاراکتری که بین رشته های "is" و "2015/12/3" بود) و سپس رشته ی
و سپس دوباره کاراکتر space چاپ میشود (همان کاراکتری که بین رشته های "2015/12/3," و "firm" بود) و سپس رشته ی "word" چاپ میشود و به خط بعدی میرود و در اولین متغییر تابع string.gsub ذخیره میشود پس در متغییر ModifiedStr رشته ی زیر ذخیره میشود :کد:"2015/12/3,"
در دومین متغییر این تابع هم تعداد مواردی که یافت شد یا همان تعداد match ها که 3 تا بودند به عنوان عدد ذخیره میشود (به عنوان عدد) (دقت کنید در صورتی که چهارمین آرگومان این تابع داده نشود) پس مقدار MatchNumber مقدار عدد 3 میشودکد:"word word 2015/12/3, word "
39)
همان مثال قبلی هست ولی در خط دوم ، چهارمین آرگومان هم به آن داده شدکد:MainStr = "Today is 2015/12/3, firm" ModifiedStr, MatchNumber = string.gsub(MainStr, "%a+", "word\n", 2) Dialog.Message("Notice", ModifiedStr.."\n"..MatchNumber , MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
در چهارمین آرگومان که الان عدد 2 دادیم ، به این معناست که مقادیر و match هایی که تا دو تای اول فقط پیدا شدند را جایگزین شود و هر چند تا match هایی که بعد از آن پیدا شدند ، کاری نداشته باشد بنابراین فقط برای دو مقدار اولی که یافت شد یعنی بجای دو match اول که یافت شد یعنی فقط بجای رشته های "Today" و "is" رشته ی "word" قرار میدهد نه بجای همه ی match های یافت شده پس به رشته ی "firm" کاری ندارد پس در متغییر ModifiedStr رشته ی زیر ذخیره میشود :
ذخیره میشودکد:"word word 2015/12/3, firm"
در متغییر MatchNumber هم تعداد match هایی که جایگزین شدند ذخیره میشود پس آرگومان شماره ی 4 در صورت وجود ، جایگزین آن میشود پس مقدارش 2 میشود
** تذکر مهم : اگر بجای آرگومان چهارم ، مقدار عددی منفی یک -1 را بدهیم ، دیگر هیچ مقداری که یافت هم شده باشد جایگزین آن نمیشود (حتی اگر آرگومان سوم که همان آرگومان بنام replace هست ، مقدار داشته باشد)
برای مشاهده این لینک/عکس می بایست عضو شوید ! برای عضویت اینجا کلیک کنید
Capture پرانتز در تابع string.gmatch :
مفهوم کپچر پرانتز در تابع string.find و بقیه ی توابع (بجز تابع string.gmatch) را میدانیم و کار کردیم ولی مفهوم این کپچر در تابع string.gmatch متفاوت است
در این تابع ، هر گاه در آرگومان pattern ها (آرگومان دوم) کپچرِ پرانتز بیاید و بعد از آن در آرگومان replacment (آرگومان سوم) ، کاراکتر یا علامت % بگذاریم و بعد از این کاراکتر ، عدد بنویسیم ، منظور همان محتوای شماره های پرانتز در آرگومان دوم هست (به تصویر بالا خوب دقت کنید) مثلا در تصویر بالا ، در آرگومان سوم ، وقتی نوشتیم 3% منظورمان محتوای پرانتزِ سوم در آرگومان دوم (که همان d% هست) ، هست. محتوای پرانتز سوم هم طبق تصویر بالا ، رشته ی "1" میشود . از آن طرف هم در آرگومان سوم (replacement) هر رشته ای که آمد جایگزین رشته ی دوم در رشته ی اول میشود پس در خروجی این تابع ، در رشته ی اصلی ، ابتدا %3 که همان رشته ی "1" هست نوشته میشود (اما در رشته ی اصلی ، رشته ی "version" ابتدا نوشته شده هست که میبینیم) و در واقع این روش برای مرتب کردن و جابجا کردن رشته ها بسیار کاربردی هست
40)
همان مثال تصویر بالاستکد:str = "version_1" NewStr = string.gsub(str, "(%a+)(%p)(%d)", "%3%1%2") Dialog.Message("Notice", NewStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
همانطور که گفته شد ، در آرگومان سوم ، وقتی نوشتیم 3% منظورمان محتوای پرانتزِ سوم در آرگومان دوم (که همان d% هست) ، هست. محتوای پرانتز سوم هم طبق تصویر بالا ، رشته ی "1" میشود . از آن طرف هم در آرگومان سوم (replacement) هر رشته ای که آمد جایگزین رشته ی دوم در رشته ی اول میشود پس در خروجی این تابع ، در رشته ی اصلی ، ابتدا %3 که همان رشته ی "1" هست نوشته میشود (اما در رشته ی اصلی ، رشته ی "version" ابتدا نوشته شده هست که میبینیم)
بعد از آن در آرگومان سوم نوشتیم 1% که به این معناست که محتوای پرانتزِ اول در آرگومان دوم را بعد از این بنویسد و پرانتز اول که +a% هست ، محتوای آنرا میدانیم که میشود رشته ی "version" پس بعد از نوشته شدن رشته ی "1" ، کاراکترِ بعد از آن ، رشته ی "version" نوشته میشود پس تا به حال ، خروجی این تابع میشود "1version"
بعد از آن هم که 2% آمد یعنی محتوای پرانتزِ دوم در آرگومان دوم که همان رشته ی "_" میشود پس خروجی این تابع (متغییر NewStr) میشود :
کد:"1version_"
41)
کد:Str = "hello Lua" NewStr = string.gsub(Str, "(.)(.)", "%2%1") Dialog.Message("Notice", NewStr, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
%2 در سومین آرگومان یعنی محتوای دومین پرانتز در آرگومان دوم . دومین پرانتز در آرگومان دوم که نقطه هست یعنی یک کاراکتر (چون علامت + ندارد که مجموع شود) و آن هم کاراکتر دوم در رشته ی اصلی چون محتوای اولین پرانتز ، کاراکتر اول در رشته ی اصلی میشود پس محتوای کاراکتر دوم رشته ی "e" میشود پس در این تابع ، اول همین نوشته میشود و محتوای کاراکتر اول رشته ی "h" میشود که بعد از آن نوشته میشود (یعنی کاراکتر اول و دوم جابجا شدند) پس خروجی این تابع تا به حال رشته ی "eh" شد
خوب همانطور که میدانیم ، این تابع ، حتی بدون اینکه در حلقه قرار گیرد ، ادامه ی آنرا هم جستجو و مقادیر آرگومان سوم را اعمال و جایگزین میکند بنابراین به سراغ سومین آرگومان به بعد میرود و همین کار را برای آنها هم انجام میدهد یعنی این بار %2 میشود محتوای چهارمین کاراکتر و %1 میشود محتوای سومین کاراکتر و اعمال و جابجا میشود یعنی کاراکتر سوم و چهارم در رشته ی اصلی ، در خروجی این تابع ، جابجا میشوند ولی چون در این کاراکترها (یعنی سومین و چهارمین کاراکتر در رشته ی اصلی) هر دو تای آنها کاراکتر "l" هستند پس جابجا شدن آنها تاثیری در خروجی ندارد بنابراین نتیجه ی تابع تا به حال میشود "ehll"
حال نوبت به کاراکترهای پنجم و ششم هست که جابجا شوند (یعنی جای کاراکتر "o" و کاراکتر space عوض میشود) بنابراین نتیجه ی تابع تا به حال میشود "ehll o"
کاراکتر هفتم و هشتم هم همینطور پس خروجی تابع میشود "ehll oul"
کاراکتر نهم هم نوشته میشود سر جای خودش چون کاراکتر دهم ای وجود ندارد در رشته ی اصلی که با آن جابجا شود بنابراین خروجی کامل و نهایی این تابع رشته ی "ehll oula" میشود که در متغییر NewStr ذخیره میشود
تابع string.gmatch :
تابع iterator ای هست که (در حلقه ی مورد نظر) ، هر بار ، مقدار و حاصل کاراکتر بعدی پترن را جستجو کرده و باز میگرداند . در واقع کار همان تابع string.find را در حلقه و با آرگومان سوم که مشخص کننده ی شماره ی کاراکتر برای جستجو بود را میکند (برای آشنایی با توابع iterator و نحوه ی استفاده از این توابع درحلقه ها ، میتوانید به مقاله ی جداگانه ای که نوشته شده ، نگاهی بیاندازید)
در آرگومان اول این تابع ، رشته ی اصلی برای جستجو و در آرگومان دوم آن pattern مورد نظر را مینویسیم
42)
بنابراین این تابع هر بار ، مقدار پترن مورد نظر را جستجو میکند و بار دیگر ، از کاراکتر بعد از کاراکترِ آخری ، شروع به جستجو کردن میکند و نتیجه را هر بار در متغییر w ذخیره میکند بنابراین 2 بار در این مثال این تابع تکرار میشود که بار اول مقدار رشته ی "hello world" و بار دوم رشته ی "from Lua" در متغییر w ذخیره میشود (برای درک درست ، حتما به مقاله ی توابع iterator سر بزنید)کد:Str = "hello world from Lua" for w in string.gmatch(Str, "%a+%s+%a+") do Dialog.Message("Notice", w, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); end
توابع هایی که در توابع قسمت string از pattern ها استفاده میکردند ، تمام شد
تابع string.format :
این تابع عملکردی شبیه به تابع string.gsub دارد و حالت و شکل یک رشته را تغییر میدهد ولی تفاوت آن این است که در آرگومان اول آن ، بجای pattern ها (که pattern ها را در آرگومان دوم string.gsub میدادیم) ، اما این بار pattern به آن نمیدهیم و چیزی بنام format میدهیم . format ها از لحاظ ظاهری شبیه pattern ها هستند ولی در کاراکترها و عملکرد اصلا شبیه آنها عمل نمیکنند بنابراین به این تابع نمیپردازیم فقط یک مثال بدون توضیح گذاشته میشود
43)
کد:d = 5; m = 11; y = 1990 Dialog.Message("Notice", string.format("%03d/%d/%d", d, m, y), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); pi=3.1418 Dialog.Message("Notice", string.format("pi = %2f",pi), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);






پاسخ با نقل قول
Bookmarks