PDA

View Full Version : حرفه ای: مورد عجیب Regex در پایتون



Mehdi Asgari
چهارشنبه 16 تیر 1389, 13:53 عصر
دیروز داشتم روی یک پروژۀ مرتبط با کرنل ویندوز کار می کردم که قرار بود یه سری کارا رو خودکار کنم (قادر به ذکر جزییات نیستم)
قسمتی از پروژه نوشتن یک پارسر برای یه سری ساختار های کوفتی کرنل بود که بعد از پارس کردن ، یه سری پارامتر generate کرده و تحویل یه برنامه ای بدم. بالطبع برنامه نویسی کرنل فقط جای سی هست (حتی سی پلاس پلاس هم نه، فقط سی. بله دیگه ، ای مگس عرصۀ سیمرغ و از این چیزا)؛ تصمیم گرفتم حداقل پارسر رو با یه زبان high level تر نوشته و در وقت صرفه جویی کنم. مطمئنا وقتی صحبت از high level میشه اول به پایتون فکر می کنم، تصمیم گرفتم از Regex استفاده کنم (پیچیدگی کار طوری بود که نمی تونستم از parser generator ها استفاده کنم، چون در واقع گرامری وجود نداشت؛ به هر حال کد ویندوزه و هزاران سورپرایز و باید تمام حالات ممکن هندل می شد). Regex ای که نوشتم یک روز کاری از وقتم رو گرفت و پیچیده ترین رجکسی هست که تا به حال نوشتم. یه جای کار متوجه چیز عجیبی شدم: وقتی یک named group بیش از یک دفعه match میشه ، فقط آخرین نمونۀ اون ذخیره شده و قابل دسترسی هست.
تکه ای از Regex ام:


(\*?(?<names>\w+))
اینجا names یک named group هست (کل این تکه ، خودش جزئی از یک گروه بزرگ تره)؛ اگه 5 کلمه توسط این گروه match بشن ، فقط آخری رو می تونیم بهش دسترسی داشته باشیم.
سرچ کردم و ظاهرا این رفتار رجکس پایتون هست و راهی برای دسترسی به match های قبلی نداریم (شاید بشه کلی کد زد و راهی پیدا کرد که بشه این کار رو کرد ولی پایتون تا جایی که من می دونم راه مستقیمی برای این کار نداره)
تصمیم گرفتم از دات نت استفاده کنم ؛ در دات نت چیزی داریم به نام Capture ؛ حالا من میگم تمام capture های یک گروه خاص رو بهم بده؛ در نتیجه تمام اون 5 کلمه رو خواهم داشت.
این دو مثال رو ببینید تا متوجه بشید چی میگم (این رجکس فقط برای مثاله و کلی ساده اش کردم. استفاده از اون در کد واقعی اصلا توصیه نمیشه. در ضمن برای مثال سادۀ پایین میشه رجکس ساده تری نوشت)
اول پایتون


import re
contents = ''' IN DWORD mehdi,
OUT UNICODE_STRING ali '''
regex = re.compile ('((IN)?\s*(OUT)?\s* ([a-zA-Z_]+) (\*?(?P<names>\w+))\s*,?)+')
matchobj = regex.search(contents)
if (matchobj):
print matchobj.group('names')

خروجی: ali
و حالا دات نت: با استفاده از Capture هم mehdi مچ میشه هم ali (از اف شارپ استفاده کردم)


open System.Text.RegularExpressions
let regex = Regex(@"((IN)?\s*(OUT)?\s* ([a-zA-Z_]+) (\*?(?<names>\w+))\s*,?)+")
let contents = "IN DWORD mehdi,\
OUT UNICODE_STRING ali "
let matches = regex.Matches contents
for c in matches.[0].Groups.["names"].Captures do
printfn "%s" c.Value


دوستان، نظر ؟

cups_of_java
چهارشنبه 16 تیر 1389, 14:43 عصر
اگه درست منظورتون رو فهمیده باشم شما با استفاده از BackReferenceها می تونید به گروه های match شده دست رسی داشته باشید.
من می دونم مثلن توی جاوا اسکریپت با /g شما تمام گروه ها رو می گیرید وگرنه فقط اولین گروه match شده رو می بینید...
توی python هم همچین حالتی داره.
http://www.regular-expressions.info/named.html

Mehdi Asgari
چهارشنبه 16 تیر 1389, 15:28 عصر
متوجه منظورم نشدید. بنده میخوام مقدار تمام repeated named group ها رو داشته باشم
http://www.regular-expressions.info/captureall.html


Backtracking information is discarded when a match is found, so there's no way to tell after the fact that the group had a previous iteration that matched abc. (The only exception to this is the .NET regex engine, which does preserve backtracking information for capturing groups after the match attempt.)
اینی که شما فرمودی (backreference) برای دسترسی به group های پیشین ، داخل خود regex به کار میره

esmaeily-hosein
پنج شنبه 09 دی 1389, 22:39 عصر
چرا از groups استفاده نکردی !

matchobj.groups('names')
print type(matchobj.groups('names'))