PDA

View Full Version : Search & replace از نوع پیشرفته و دقیق و مطمئن



freeman99
دوشنبه 18 اسفند 1393, 00:22 صبح
من با نوتپد++ کار میکنم. از ویژگی Search & replace مجهز و منعطف و سریعی که داره زیاد استفاده میکنم.
یکی از قابلیت های خیلی بدردبخورش اینه که میتونی بهش سرچ بدی که توی تمام فایلهای تحت یک فولد و زیرفولدرهاش میگرده و پیدا میکنه و میتونه بصورت خودکار اون رشته رو با رشتهء دیگری در تمام فایلها جایگزین و سیو کنه. این قابلیت خیلی به درد من میخوره و زیاد ازش استفاده کردم تاحالا. الانم مثلا یه رشته ای رو میخوام با رشتهء دیگه جایگزین کنم، فرض کنید مثلا یک تابع دارم به اسم tr که میخوام اسمش رو تغییر بدم و هرجا این تابع اومده اون رو با رشتهء دیگری عوض کنم. مجموعا به تعداد 772 بار این تابع در کد من استفاده شده! حالا فک کنید بخوام دونه دونه اینا رو حتی نگاه کنم، کلی وقت و زحمت میبره و با اینحال امکان خطای انسانی هم زیاده که یوقت یکی یا چندتاش از زیردستم در بره یا اشتباه دیگری بکنم. بنابراین من میخوام از قابلیت های پیشرفته و خودکار برای جستجو و تغییر اینطور چیزا استفاده کنم.
البته خیلی از اینطور کارهای پیشرفته و دقیق رو معمولا بدون سرچ از نوع رگولار اکسپرشن نمیشه انجام داد! خوشبختانه نوتپد++ امکان چند نوع جستجو منجمله رگولار اکسپرشن رو داره.
باوجودی که به رگولار اکسپرشن تسلط دارم اما نوشتن رگولار اکسپرشن های کاملا دقیق و مطمئن کار راحتی نیست و قبلش چند مدل باید تست و بررسی کنی که درست کار کنه و چیزی نباشه که بحساب نیاورده باشی، و با اینحال هنوزم یک ذره بالاخره شک میکنی که نکنه تک و توک رشته های دیگری در این همه کد وجود داشته باشن که توسط Search & replace ما به اشتباه شناسایی و جایگزین بشن. بدبختی اینه ممکنه اصلا متوجه نشی و برنامه هم ظاهرا بدون مشکل کار کنه و گاهی این موارد حتی از تست های نهایی هم ممکنه بدون آشکار شدن عبور کنن و اینطوری یه برنامه با باگهای پنهان بوجود میاد! تازه همون مواردی هم که در مراحل توسعه و تست مشخص میشن فهمیدن علت واقعی اونا گاهی میتونه سخت و آزاردهنده باشه (اینکه بتونی بفهمی سرمنشاء فلان رفتار و باگ غیرعادی در یک جای برنامه درواقع از جایگزینی اشتباه یه چیزی در یه جای دیگر برنامه ناشی شده، لزوما در تمام موارد چیز روشن و راحتی نیست).
البته من بهرحال در کل باید یک بار یا حتی بیشتر خط به خط کدهای برنامم رو بخونم، ولی با این حال این کافی نیست و احتمال خطای انسانی همچنان بالاست. تا میشه باید هرکاری رو خودکار کرد، ولی با حداکثر دقت و اطمینان.

میخواستم بدونم شما چنین کارهایی رو چطوری انجام میدید و آیا نظیر چنین مشکلاتی داشتید یا نه و نظر و ایدهء احتمالی شما در این مورد چیه.

Search & replace دقیق خودش یه مهارته! چون حالات و موارد پیچیده و غیرمنتظره ای در این ارتباط میتونن براحتی رخ بدن.
مثلا من اول با رشتهء سادهء tr(‎ برای تابع خودم سرچ کردم که 888 تا نتیجه داد. میدونستم که این احتمالا موارد غیرمرتبطی رو هم گرفته، بنابراین با بررسی بیشتر متوجه شدم که مثلا عبارت substr(‎ هم توسط این جستجو مچ شده. اما ترفند بعدی این بود که از tr('‎ استفاده کردم، که این بار نتایج 760 مورد شد. این خیلی خوبه که آدم در کدش از یک استاندارد و روش ثابت پیروی کنه، چون مثلا من میدونستم که هرجا رشته ای رو برای این تابع فرستادم، از کوتیشن تکی استفاده کردم. ولی بعدش گفتم بذار با کوتیشن دوبل هم چک کنم که مطمئن بشم. بنابراین با tr("‎ سرچ کردم و دیدم بله یک مورد هم کوتیشن دوبل بوده که البته دلیلی نداشت دوبل باشه و از زیر دستم در رفته بوده. من اون کوتیشن دوبل رو تبدیل به تکی کردم تا از زیر سرچ ها در نره و مشکل ساز نشه. خب این تا اینجا. ولی حالا از کجا معلوم شاید substr("‎ یا substr('‎ هم در کدهام داشته باشم که توسط این سرچها مچ میشن، بنابراین رشته های substr("‎ یا substr('‎ رو هم سرچ کردم و دیدم نه خوشبختانه چنین رشته هایی در کدهام ندارم. اما با این حال هنوزم عبارت tr('‎ برای عملیات خطرناک Search & replace مناسب نیست، چون بازم این احتمال هست که رشتهء دیگری در کدها باشه که به tr('‎ بشه اما تابع tr من نباشه!
سرچ دقیق پیچیدگی های زیادی داره. نهایتا به رگولار اکسپرشن سویچ کردم تا بتونم معیارهای بیشتری رو در سرچ مشخص کنم. مثلا ‎[^s]tr\(‎ تعداد نتایج 772 رو برمیگردونه. به زبان ساده این رگولار اکسپرشن میگه رشته هایی مثل tr(‎ که کاراکتر قبلشون s نباشه! اینطوری دیگه substr ها مچ نمیشن. ولی اگر دقت کنید ما کوتیشن رو کلا برداشتیم، و نتایج جستجو هم باید 761 مورد باشه ولی بجاش 772 مورد شده، یعنی 11 تا اضافه شده! چرا؟ آیا ما رشته های دیگری رو به اشتباه مچ کردیم؟ نه، قضیه اینه که بعضی فراخوانی های تابع tr از متغیر استفاده کرده بودن و نه رشتهء صریح، یعنی مثلا tr($field_name)‎. ما با رگولار اکسپرشن ‎[^s]tr\(\$‎ تعداد این موارد رو میشماریم و چون تعداد نتایج این جستجو کمه میتونیم نتایج رو یک به یک با چشم بررسی کنیم که بینشون رشته های نامربوط مچ شده وجود نداشته باشه، که خوشبختانه اینطور نبود. خب ما تاحالا و بعد از کلی تست و آزمون و خطا به رگولار اکسپرشن ‎[^s]tr\(‎ رسیدیم که بنظر بد نمیاد، ولی بنظر من هنوزم بقدر کافی دقیق و مطمئن نیست، مثلا از کجا معلوم شاید جایی در کد رشته ای مشابه این الگو باشه که بجای s با حرف یا کاراکتر دیگری شروع شده باشه ولی تابع tr مورد نظر ما نباشه. خیلی نمیشه مطمئن بود، ولی تمام حالات ممکن رو حضور ذهن داشتن و تست کردن هم در عمل کار تقریبا غیرممکنیه! پس باید تا میتونیم محکم کاری کنیم و بقیه رو به خدا بسپاریم :لبخند:
اینجا با خودم فکر کردم کاش اسم تابع tr رو گذاشته بودم translate یا حداقل trans که اینطوری میتونستم سرچ مطمئن تری داشته باشم چون طبیعتا احتمال اینکه دو رشته کاراکترهای متوالی یکسان اینقدر بیشتری داشته باشن خیلی کمتره. ولی خب از یک طرف هم راحتی و سرعت موقع کدنویسی به جای خودش مزیت و خواستنی است! اما اینکه میگن از نامهای کامل و پرمحتوی یا حداقل اینکه بیش از حد کوتاه و مبهم نباشن استفاده کنید، یکی از مزایاش هم در اینطور موارده انگار!!
اوه اوه تازه یه مشکل اساسی تر رو یادم رفت! مشکل اینه که رگولار اکسپرشن ‎[^s]tr\(‎ رو اصلا نمیتونم برای Search & replace استفاده کنم، برای سرچ تنها میشه ولی برای Search & replace نمیشه، چون این الگو یک کاراکتر قبل از tr رو هم مچ میکنه و من هرچی در فیلد Replace with بذارم نه تنهای جای tr(‎ رو میگیره، بلکه جای این کاراکتر رو هم میگیره و این کاراکتر که دقیقا مشخص نیست چه کاراکتری خواهد بود به اشتباه حذف میشه!
بررسی کردم اما متاسفانه ظاهرا نوتپد++ امکان قراردادن Backreference در این موارد رو نداره (اینجا دیگه نوتپد++ هم کم آورد :متفکر:). Backreference یعنی ما یک چیزی مثل Place holder در عبارت جایگزین میذاریم که با یک رشتهء خاص مچ شده با قسمتی از الگوی جستجو جایگزین میشه. این امکان مثلا در تابع preg_replace خود PHP وجود داره. بنابراین به فکر افتادم که شاید باید یک برنامهء PHP برای این کار بنویسم که بجای نوتپد++ برای اینطور Search & replace های خاص ازش استفاده کنم.

ویرایش: نه الان سرچ کردم متوجه شدم که ظاهرا نسخهء نوتپد++ من چون زیاد جدید نیست این مشکل رو داره، و نسخه های جدیدتر نوتپد++ امکان استفاده از Backreference رو دارن. الان میرم آخرین نسخه رو دانلود میکنم :لبخندساده:

راستی در جریان این جستجوها باید حواس به این باشه که کدوم نوع سرچ فعاله، چون اگر نوع سرچ شما با رشته ای که جستجو میکنید هماهنگ نباشه طبیعتا نتایج اشتباه میده که گاهی ممکنه متوجه نشید و شما رو گمراه کنه. ضمنا موقع Search & replace نهایی اگر این اشتباه رو بکنید ممکنه کل کدهاتون رو داغون کنه و دیگه درست شدنی هم نباشه، بنابراین همیشه قبل از هر مرحله عملیات خطرناک، یک بکاپ از فایلهای فعلی بگیرید تا درصورت لزوم بتونید همه رو مجددا جایگزین کنید.

freeman99
دوشنبه 18 اسفند 1393, 00:33 صبح
راستی الان به ذهنم رسید که احتمالا IDE های بزرگ تر مثل ویژوال استودیو امکاناتی برای این قبیل کارها داشته باشن. مثلا بهش بگیم برو هرجا تابعی با این اسم دیدی اسمش رو به فلان تغییر بده. منظورم اینه خودش کد رو تفسیر کنه و تشخیص بده که فلان بخش الان درواقع یک تابع هست و اسمش چیه، و اون رو با رشته های دیگر تفکیک کنه. ولی نوتپد++ همهء کد رو بصورت رشته میبینه و از ساختار و اجزای خود برنامه اطلاعاتی نداره.
البته حتی با این وجود اگر چنین امکاناتی در محیطهای توسعهء دیگر هم باشه، بنظرم بازهم لزوما پاسخگوی تمام موارد و نیازها نیست. مثلا خود من الان کاری که میخوام بکنم دقیقا این نیست که اسم یک تابع رو عوض کنم، بلکه میخوام اسم تابع رو با یک رشتهء خاصی عوض کنم که بخشهایی از اون جزو خود اسم تابع نیست. بنابراین اگر مثلا ویژوال استودیو چنین امکانی داشته باشه که بتونه اسم توابع رو بصورت خودکار در سراسر کد عوض کنه، احتمالا بازم نمیتونه کاری رو که مد نظر منه انجام بده (ولی فکر میکنم بشه با ترفند غیرمستقیمی، از همین امکان برای رسیدن به هدف نهایی استفاده کرد).

MMSHFE
دوشنبه 18 اسفند 1393, 08:05 صبح
توی IDEهایی مثل NetBeans و Zend Studio قابلیتی هست به اسم Refactor که به شما اجازه پیدا کردن مکانهای فراخوانی توابع، متدهای کلاس و... رو میده و میتونید با تغییر نام متد یا کلاس، با کمک Refactor تمام فراخوانی ها رو ببینین و اونایی که نمیخواین تغییر کنن، تیکشون رو بردارین. یک فهرست دو ستونه میاره که توی ستون اول فراخوانیها (با مسیر کامل فایل) رو میاره و روی هرکدوم کلیک کنید، توی ستون دوم اون قسمت از کد رو نشونتون میده تا اگه مطمئن شدین توی جستجوی خودکار، اشتباهاً اون مورد رو پیدا کرده، تیکش رو بردارین.