منبع اصلی نوشتار زیر در این لینک قرار دارد

Regex، ابزاری کاربردی در ویرایشگر‌های متنی

یکی از امکانات جالبی که ویرایشگر‌های متن پیشرفته دارن، جستجو برای «عبارات منظم» که خیلی به درد برنامه نویسی و کلن ویرایش متن می‌خوره. با این امکان می‌شه به جای جست و جو برای یک متن، یک الگو رو جست و جو کرد. بذارین با یک مثال نشون بدم بهتون. کد زیر رو (کد جاوا) ببینید (این کد فقط برای مثاله، از نظر منطقی ممکنه به درد نخوره):

public class StatePropertiesPanel extends JPanel implements StateProperties {
  
        private JComboBox statesComboBox;
        private JComboBox zeroStateComboBox;
        private JButton zeroStateColorButton;
        private JComboBox oneStateComboBox;
        private JButton oneStateColorButton;
        private JTextField nameTextField;
        private JCheckBox isFinalStateCheckBox;
        private JButton addButton;
        private JButton removeButton;
        private JButton applyButton;
        private Graph graph;
        private JButton openButton;
        private JButton saveButton;
        private Action openAction;
        private Action saveAction;
  
}

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

public class StatePropertiesPanel extends JPanel implements StateProperties {
  
        private JComboBox statesComboBox;
        private JComboBox zeroStateComboBox;
        private JButton zeroStateColorButton;
        private JComboBox oneStateComboBox;
        private JButton oneStateColorButton;
        private JTextField nameTextField;
        private JCheckBox isFinalStateCheckBox;
        private JButton addButton;
        private JButton removeButton;
        private JButton applyButton;
        private Graph graph;
        private JButton openButton;
        private JButton saveButton;
        private Action openAction;
        private Action saveAction;
  
        public StatePropertiesPanel(JComboBox statesComboBox,
                                JComboBox zeroStateComboBox,
                                JButton zeroStateColorButton,
                                JComboBox oneStateComboBox,
                                JButton oneStateColorButton,
                                JTextField nameTextField,
                                JCheckBox isFinalStateCheckBox,
                                JButton addButton,
                                JButton removeButton,
                                JButton applyButton,
                                Graph graph,
                                JButton openButton,
                                JButton saveButton,
                                Action openAction,
                                Action saveAction) {
                this.statesComboBox = statesComboBox;
                this.zeroStateComboBox = zeroStateComboBox;
                this.zeroStateColorButton = zeroStateColorButton;
                this.oneStateComboBox = oneStateComboBox;
                this.oneStateColorButton = oneStateColorButton;
                this.nameTextField = nameTextField;
                this.isFinalStateCheckBox = isFinalStateCheckBox;
                this.addButton = addButton;
                this.removeButton = removeButton;
                this.applyButton = applyButton;
                this.graph = graph;
                this.openButton = openButton;
                this.saveButton = saveButton;
                this.openAction = openAction;
                this.saveAction = saveAction;
        }
}

خب اگر بخوایم دستی این تغییرات رو اعمال کنیم، هم وقت گیر می‌شه و هم احتمال خطا بالا می‌ره. اما استفاده از «عبارت منظم» کار رو برای ما خیلی راحت می‌کنه. بذارین قبل از این، چند‌تا نرم افزار که این قابلیت رو پشتیبانی می‌کنن، معرفی کنم.

برای ویندوز اول:

  • Notepad++ برنامه‌ی خیلی قوی و متن باز و البته رایگان.
  • Geany که یک برنامه‌ی در ابتدا گنو/لینوکسی بوده ولی به لطف متن‌باز بودن، نسخه‌ی ویندوزی اون هم وجود داره.
  • MEdit که برنامه‌ی بدی به نظر نمیاد. گنو/لینوکسیه ولی برای ویندوز کامپایل شده.
  • Netbeans هم که یک محیط مجتمع قوی به حساب میاد.
  • Vim که هرچی در موردش بگم کم گفتم. البته خودم خیلی خوب بلد نسیتم باهاش کار کنم، ولی میلاد توی وبلاگش متن‌های خوبی در این مورد نوشته
  • و خیلی برنامه‌های دیگه و تقریبن تمام برنامه‌هایی که این پایین برای لینوکس معرفی می‌کنم که نسخه‌ی ویندوزی هم دارن. فعلن همینا کافیه.

و برای لینوکس:

  • Geany
  • MEdit
  • Kate
  • KWrite
  • Netbeans
  • Vim
  • Bluefish
  • و خیلی‌های دیگه.

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

private JComboBox statesComboBox;
private JComboBox zeroStateComboBox;
private JButton zeroStateColorButton;
private JComboBox oneStateComboBox;
private JButton oneStateColorButton;
private JTextField nameTextField;
private JCheckBox isFinalStateCheckBox;
private JButton addButton;
private JButton removeButton;
private JButton applyButton;
private Graph graph;
private JButton openButton;
private JButton saveButton;
private Action openAction;
private Action saveAction;

از منوی Edit ویرایشگر متنم که الان Kate است، Replace رو انتخاب می‌کنم. برای نرم‌افزار‌هایی که معرفی کردم، ممکنه این گزینه توی منو‌هایی مثل Search یا Find هم باشه. من باید این کار رو کنم، اول private‌ها حذف بشه، بعد «;» رو باید با «,» تعویض کنم، در آخر هم public StatePropertiesPanel( رو به ابتدا و ({} رو به انتها اضافه کنم. این کار آخر که آسونه و می‌شه با دست انجام داد، اما برای بقیه‌ی کار، Replace رو باز می‌کنم، گزینه‌ی Regular expression رو فعال می‌کنم و توی کادر Find عبارت زیر رو می‌نویسم (بعد از این توضیح می‌دم که معانی این عبارات چی هستن):

private (.*);

و توی کادر Replace مقدار زیر رو می‌نویسم:

\t\1,

و در انتها دکمه‌ی Replace All رو می‌زنم. نتیجه به شکل زیر می‌شه:

        JComboBox statesComboBox,
        JComboBox zeroStateComboBox,
        JButton zeroStateColorButton,
        JComboBox oneStateComboBox,
        JButton oneStateColorButton,
        JTextField nameTextField,
        JCheckBox isFinalStateCheckBox,
        JButton addButton,
        JButton removeButton,
        JButton applyButton,
        Graph graph,
        JButton openButton,
        JButton saveButton,
        Action openAction,
        Action saveAction,

خب، کامای خط آخر رو پاک می‌کنم و به جای اون می‌نویسم ({} و ابتدای خط اول public StatePropertiesPanel( رو اضافه می‌کنم. تا اینجا که متد سازنده‌ی من شکل کلی رو به خودش گرفت. پس متن رو کپی می‌کنم به سورس فایلم. اما حالا محتوایت این متد سازنده. دوباره مقایر رو مثل چند مرحله قبل به یک فایل جدید (و موقت، راه‌های دیگه هم هست، ولی این از همه دم دست تره!) انتقال می‌دم و مراحل رو اینبار به این شکل تکرار می‌کنم. استراتژی کار این بار به این شکله. private ها و اسم کلاس‌هایی که شیء از اون ساخته می‌شه باید حذف بشه، اسم شیء باید با اسم فیلد تغییر کنه (یعنی یه this. بیاد قبلش) و بعد مساوی و باز اسم شیء به عنوان پارامتر متد. پس توی کادر Find این رو می‌نویسم:

private .* (.*);

و بعد توی کادر Replace می‌نویسم:

\t\tthis.\1 = \1;

و Raplace all رو می‌زنم. نتیجه می‌شه:

                this.statesComboBox = statesComboBox;
                this.zeroStateComboBox = zeroStateComboBox;
                this.zeroStateColorButton = zeroStateColorButton;
                this.oneStateComboBox = oneStateComboBox;
                this.oneStateColorButton = oneStateColorButton;
                this.nameTextField = nameTextField;
                this.isFinalStateCheckBox = isFinalStateCheckBox;
                this.addButton = addButton;
                this.removeButton = removeButton;
                this.applyButton = applyButton;
                this.graph = graph;
                this.openButton = openButton;
                this.saveButton = saveButton;
                this.openAction = openAction;
                this.saveAction = saveAction;

خب، به همین راحتی این‌ها هم تولید شد.

با یک مثال شروع کردم که حرف‌هام ملموس باشه. این الگو، قواعد خودش رو داره و قواعد متفاوتی داره. من POSIX Basic Regular Expressions همراه با POSIX Extended Regular Expressions رو توضیح می‌دم. یک عبارت منظم یک مجموعه از رشته‌ها رو مطابق می‌شه که می‌تونیم از این مجموعه برای پیدا کردن متن مورد نظر استفاده کنیم. ما یک سری Metacharacter (که حالا فارسی بگیم ابرحرف) داریم که معنی خاصی دارن. اولی این‌ها رو یاد بگیریم:

  • «.» مطابق می‌شه با یک حرف. البته در اکثر بنامه‌ها این حرف شامل خط جدید نمی‌شه
  • «[ ]» مطابق یکی از حروف مشخص‌شده یا شامل شده می‌شه. مثلن [abc] مطابق می‌شه با حرف a یا b یا c و [a-z] مطابق هر حرف از حروف کوچیک الفبای انگلیسی می‌شه. [abx-z] مطابق a یا b یا x یا y یا z می‌شه. [a\-b] هم مطابق a یا – یا b می‌شه. \ اینجا برای تغییر معنی به کار می‌ره. [a-dx-z] هم مطابق می‌شه با a یا b یا c یا d یا x یا y یا z می‌شه. یعنی می‌شه بازه‌ها رو ترکیب کرد.
  • «[^ ]» مطابق هر حرفی به‌جز حرف‌های درون براکت می‌شه. به عنوان مثال [^abc] تمام حروف به‌جز a و b و c خواهد بود. قواعد براکت معمولی رو هم می‌شه به کار برد
  • «^» معنی موقعیت یک رشته درون رشته‌ی دیگه رو می‌ده که این معنی برای ابزار‌هایی که خط در اونها معنی داره (مثل ویرایشگر‌های متنی) به معنی شروع خط خواهد بود.
  • «$» معنی موقعیت پایان رشته و در ابزار‌های خطی، به معنی پایان خط هست. مثال‌های انتها معنی رو بهتر روشن می‌کنه.
  • «()» یک عبارت علامتگذاری شده رو مشخص می‌کنه. شما می‌تونید توی پرانتز یک عبارت منظم رو که براتون مهمه، مثل نام شیء تو مثال اول مطلب، علامت بزنید تا از اون استفاده کنید. قسمت زیر هم مرتبط با این قستمه.
  • «\n» که n یک عدد بین ۱ تا ۹ هست که مطابق می‌شه با nامین رشته‌ی علامت گذاری شده با پرانتز. بعضی ابزار ها اجازه‌ی گستش این دستور رو بیشتر از ۹ فراهم می‌کنن.
  • «*» مطابق می‌شه با رخداد ۰ یا بیشتر از حرف ماقبل این علامت. به این معنی که ab*c رو اگر برای عبارت منظم داشته باشیم، مطابق می‌شه با ac و abc و abbc و abbbc و….
  • «+» مطابق می‌شه با رخداد ۱ یا بیشتر از حرف ماقبل این علامت. به این معنی که ab+c رو اگر برای عبارت منظم داشته باشیم، مطابق می‌شه با abc و abbc و abbbc و….
  • «?» مطابق می‌شه با رخداد ۰ یا یک باز از حرف ماقبل این علامت. به این معنی که ab? مطابق a و ab خواهد بود.
  • «|» برای اجتماع کردن دو عبارت منظم می‌شه. یا به عبارتی برای انتخاب بین دو عبارت منظم به کار می‌ره. مثلن abc|def مطابق abc و def می‌شه.
  • «{n,m}» هم مطابق با n تا m تکرار از حرف ما قبل خودش می‌شه. این عبارت ممکنه توسط ابزار‌های قدیمی تر پشتیبانی نشه. مثال هم: ab{1,3} مطابق می‌شه با ab و abb و abbb.

و البته اگر برنامه‌نویسی هم کرده باشین، مژده اینه که می‌تونید از حروف خاص مثل \t یا \n هم استفاده کنید.

اما با چند مثال بیشتر مفهوم رو گسترش می‌دم.

.at

مطابق تمام رشته‌های ۳ حرفی شامل at می‌شه. مثل cat و bat و hat و حتی 2at

[hc]at

مطابق hat و cat می‌شه و نه چیز دیگه‌ای.

[^b]at

مطابق تمام رشته‌های ۳ حرفی به‌جز bat می‌شه.

^[hc]at

مطابق تمام cat و hat ها در اول خط می‌شه.

[hc]at$

مطابق تمام cat و hat های انهای خط می‌شه.

[hc]?at

مطابق hat و cat و at می‌شه.

.*=

مطابق تمام رشته‌هایی که به = ختم می‌شن، می‌شه.

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

(.*)=(.*)

طبق فرض، پرانتز اول برابر کلمه‌ی اول و پرانتز دوم برابر کلمه‌ی دومه. حالا برای جایگزینی، کادر Replace رو با عبارت زیر پر می‌کنم:

\2=\1

به همین راحتی. بذارین یه مثال خیلی کاربردی برای وبلاگ نویسا بگم. می‌خوایم قبل تمام لینک‌های موجود توی یک متن، favicon اون رو (همون آیکون کوچیکه که کنار نوار آدرس توی مرورگر دیده می‌شه) رو با استفاده از «سرویس گوگل برای بدست آوردن favicon ها و اعمال آن بر لینک های خارجی» قرار بدیم. تقریبن تمام ابزار‌هایی که برای نوشتن وبلاگ استفاده می‌شه (مثل ویرایشگر خود وبلاگ(ها) و Bilbo Blogger) این امکان رو به شما می‌دن که بعد از نوشتن متن، کد HTML اون رو ببینید. پس شما اول متن رو بنویسید و کد HTML اون رو به یک ویرایشگر متن انتقال بدین تغییرات رو اعمال کنید و باز کد رو از ویرایشگر متن به وبلاگ منتقل کنید. خب اول در مورد لینک برای کسانی که آشنایی ندارن، این رو بگم.

وقتی شما یک لینک می‌سازین، برچسبی قبل و بعد اون قسمت لینک اضافه می‌شه. به عنوان مثال لینک «سار» رو شرح می‌دم:

<a href="http://saarblog.wordpress.com">سار</a>

خب، برچسب <a> معرفی کننده‌ی شروع یک لینکه. خصوصیت href از اون مشخص کننده‌ی آدرس لینکه. و برچسب با </a> تموم می‌شه. ما باید domain مربوط رو پیدا کنیم. این domain قسمت ابتدای URL آدرس ماست و یا تا اولین / و در صورت نبودن اولین /، تا آخر URL ادامه داره. از اونجا که حروف بزرگ و کوچیک تفاوتی ایجاد نمی‌کنه، پس می‌تونیم برچسب <A> رو هم داشته باشیم. اول عبارت منظم:

<[aA] ([^>]*[hH][rR][eE][fF]="?[hH][tT][tT][pP][sS]?)://([^/" ]+)("?[^>]*)>

خب برای این که یک لینک شروع بشه، باید <a یا <A رو داشته باشیم که من از <[aA] استفاده کردم. بعد از اون می‌تونه هر عبارتی بیاد به‌جز «>» و عبارت هم برای ما اهمیت داره (برای این که ساختار لینک از بین نره) ولی حتمن باید یک فاصله بعد از a یا A باشه (وگرنه ممکنه یه برچسب دیگه باشه!). عبارت href= شروع آدرس URL رو نشون می‌ده که ممکنه (و در اکثر موارد) این URL بین دو «»» قرار داره. برای همین بعد از «»» یک علامت سوال قرار دادم. پروتکل می‌تونه http یا https باشه، و از اونجا که می‌شه اینطور هم نوشت: HtTpS، من کاراکتر به کاراکتر اون‌ها رو توی براکت قرار دادم. بعد از اون حتمن باید :// وجود داشته باشه، بعد Domain از اینجا تا اولین «/» یا «»» یا فاصله ادامه خواد داشت. و بعد ادامه تا علامت «>». خب، حالا عبارت Replace باید به این شکل ساخته بشه:

<a \1://\2\3><img src="http://www.google.com/s2/favicons?domain=\2" />

پرانز دوم Domain بود، برای همین جاهایی که نیاز بود، \2 رو استفاده کردم. جالب بود، نه؟

فکر می‌کنم الآن می‌تونید مثال‌های برنامه نویسی رو متوجه بشین.
پ‌ن: این ترفند favicon رو برای این پست اعمال کردم.

=-=-=-=-=
Powered by Bilbo Blogger, Made with Bluefish HTML editor.




برچسب ها : , , ,