ورود

View Full Version : آموزش: استفاده از Regular Expressions یا به اختصار RegEx برای جستجو و جایگذاری عبارت در متن



eb_1345
سه شنبه 01 آبان 1403, 15:25 عصر
با سلام
در این تاپیک ان شاءالله سعی خواهیم کرد در خصوص عبارت Regular Expressions که به اختصار RegEx نامیده میشه مطالبی رو ارائه نمائیم .
البته پیشاپیش یادآوری کنم که بنده شخصاً بواسطه گستردگی ای که در پترن های این آبجکت وجود داره در کد نویسی ها خیلی سراغ استفاده از این آبجکت نرفته ام و به همین خاطر باید عرض کنم که تسلط و میزان آشنائی بنده نسبت به این موضوع ناچیزه . به همین خاطر از دوستان با تجربه ای که در این زمینه اطلاعات مفیدی دارن خواهش میکنم در صورت تمایل در ادامه مباحث این تاپیک با ما همراه باشن !
ضمناً لازمه یادآوری کنم که برای استفاده از آبجکت فوق نیاز به انتخاب رفرنس های مشخص شده در تصویر زیر میباشه و یا اون رو بصورت زیر با استفاده از تابع Create-object در ابتدای کدها معرفی و ایجاد نمائیم


Dim objRegExp
Set objRegExp = CreateObject("VBScript.RegExp")

156247

و اما توضیح مختصری که در رابطه با Regular Expressions یا بطور اختصار RegEx لازمه بعنوان مقدمه ارائه بشه :
ترجمه فارسی Regular Expressions میشه عبارات با قاعده یا منظم
به طور ساده ، RegEx رشته هایی هستن که برای انجام عملیات تطابق و جایگذاری عبارات در رشته استفاده میشه. به عبارتی ما از RegEx برای دو منظور استفاده می کنیم :
۱- جستجوی عبارات در متن
۲- جستجو و جایگزاری عبارت در متن
فعلاً برای پست اول به همین مقدار توضیحاتی که ارائه شد بسنده می کنم تا ان شاءالله در پست های بعد همراه با مثال با این آبجکت و کاربردهای اون بیشتر آشنا بشیم
....

eb_1345
سه شنبه 01 آبان 1403, 16:19 عصر
سهواً در عنوان تاپیک بجای کلمه اختصار اخصار تایپ نموده ام که از این بابت عذرخواهی می کنم

eb_1345
سه شنبه 01 آبان 1403, 16:26 عصر
آشنائی با کاراکتر های مورد استفاده در RegEx در جدول زیر:
156248

moustafa
سه شنبه 01 آبان 1403, 21:39 عصر
دستتون درد نکنه جناب بهرامی عزیز
در متون فارسی هم کارایی داره ؟

eb_1345
سه شنبه 01 آبان 1403, 22:17 عصر
مطالعه متن زیر به ما کمک میکنه که شناخت و درک بهتری نسیت به استفاده و بکارگیری این ابزار در داده های متنی پیدا کنیم :
عبارات با قاعده یا Regular Expression (RegEx)، ابزاری برای جستجو و تطبیق الگوها در متن است که به ما این امکان را می‌دهد که به سادگی تمام عبارات از داخل متن که الگوهای مشخصی مثل شماره تلفن، شماره کارت و … دارند را استخراج کرده، تغییر دهیم یا روی آن پردازش انجام دهیم.
از RegEx می‌توان برای تأیید و اعتبارسنجی الگوهای خاص مثل ایمیل، شماره همراه، شماره کارت، کد ملی و … در ورودی کاربر استفاده کرد. به عنوان مثال، در یک متن می‌توانیم خیلی راحت تمام ایمیل‌های کاربران را به تفکیک استخراج کنیم و برای آنها ایمیل تبلیغاتی ارسال کنیم. یا شماره تلفن های یک سایت را استخراج و ذخیره کرده و یا اینکه هر شماره کارتی که کاربر در متن دیدگاه‌ها ارسال کرده را تشخیص و به جای نمایش کامل، آن را ستاره دار نمایش دهیم. و کلی کارهای جالب دیگر مثل تبدیل تمام تاریخ های میلادی به شمسی در داخل متن، لینک کردن شناسه کاربری که داخل متن منشن شده و …

پس نتیجه می گیریم اولاً، این ابزار به ما امکان می‌ده تا به‌طور کارآمدتری با داده‌های متنی کار کنیم. ثانیاً، آموزش regex یا عبارات با قاعده میتونه باعث صرفه‌جویی در زمان کد نویسی ما بشه ، چرا که میتونیم با بکارگیری این ابزار کارهای پیچیده‌ای مانند اعتبارسنجی و تجزیه داده‌ها را با استفاده از یک خط کد براحتی انجام بدیم
شاید بدون استفاده از این ابزار هم بشه از راه های دیگه مواردی که در متن بالا به اون ها اشاره شد انجام داد اما طبق تجربه بسیار امکان داره که استفاده از راههای دیگه نیازمند نوشتن کدهای زیادتر و به تبع صرف زمان بیشتری باشه. بنابراین استفاده از آبجکت RegEx یا عبارات با قاعده گزینه به مراتب سریع، بهتر و منعطف تری میتونه محسوب بشه

eb_1345
سه شنبه 01 آبان 1403, 22:33 عصر
دستتون درد نکنه جناب بهرامی عزیز
در متون فارسی هم کارایی داره ؟

خواهش میکنم
امتحان نکرده ام ولی به احتمال زیاد در متون فارسی هم باید کارائی داشته باشه .
ان شاء الله به کمک دوستان در ادامه مثال ها و نمونه های کاربردی اعم از فارسی یا انگلیسی رو بررسی و امتحان می کنیم

eb_1345
چهارشنبه 02 آبان 1403, 11:35 صبح
فکر کنم به اندازه کافی در مورد RegExp و کاربردهای اون توضیح داده شد و دیگه وقت اون رسیده کاربردهای این آبجکت رو در قالب مثال مشخص نمائیم
بر روی فرم یک کمند باتن ایجاد کنید و کدهای زیر رو در رویداد کلیک اون قرار بدهید :

Dim regx
Set regx = CreateObject("VBScript.RegExp")
regx.Pattern = "vba"
MsgBox regx.Test("vba is my language")

در دو خط ابتدائی همونطور که مشخصه آبجکت RegExp معرفی و ایجاد شده.
و اما در مقابل regx.Pattern که مهمترین و حساس ترین قسمت RegExp میباشه باید یک عبارت منظم و باقاعده بعنوان پترن یا الگو ایجاد کنیم که بر اساس ساختاری که برای اون در نظر می گیرم عملیات جستجو و تطبیق رشته یا متن مورد نظرمون صورت بگیره تا یک خروجی به ما ارائه بشه
کاراکترهائی که در تصویر جدول پست 3 مشاهده می کنین بر اساس قاعده خاص خودشون در قسمت Pattern این آبجت بکار میرن
در نهایت نتیجه جستجو و تطبیق رشته با الگو بعنوان خروجی در کد خط آخر در دو حالت True یا False مشخص میشه.
در مثال فوق باید متن یا رشته مورد نظرمون رو در قسمت Text آبجکت وارد بشه . ما در این قسمت متن vba is my language وارد کرده ایم و در قسمت پترن هم عبارت vba . این یعنی اینکه ما از آبجکت RegExp میخوایم بره توی متن vba is my language بگرده و جستجو کنه که آیا سه حرف متوالی vba بر اساس قاعده و ساختاری که برای عبارت پترن در نظر گرفته ایم در متن فوق وجود داره یا نه و نتیجه رو با True یا False نمایش بده
ببخشین دوستان داره اذان پخش میشه ، اگه ریا نباشه من فعلاً برم نمازمو بخونم :لبخند:

eb_1345
چهارشنبه 02 آبان 1403, 12:46 عصر
در پست قبل برای عبارت پترن قاعده و ساختار خاصی بکار نبرده بودیم و خروجی هم True بود ، چون سه حرف متوالی vba در متن vba is my language وجود داشت
حالا ما ی کوچلو ساختار پترن مثال قبل رو تغییر میدم . بدینصورت که میایم vba رو بصورت حروف بزرگ درج میکنیم و خروجی میگیریم. می بینیم که در این حالت نتیجه بصورت false نمایش داده میشه . چون vba با حرف بزرگ در متن وجود نداره
پس ما اگه میخواهیم عبارت الگو به بزرگ و کوچک بودن حروف حساس نباشه میایم خصوصیت IgnoreCase آبجکت رو در حالت True قرار میدیم. یعنی نادیده گرفتن حساسیت به حروف کوچک و بزرگ
بنابراین با اضافه نمودن خصوصیت فوق کدها بصورت زیر تغییر میکنه :


Dim regx
Set regx = CreateObject("VBScript.RegExp")
regx.IgnoreCase = True
regx.Pattern = "VBA"
MsgBox regx.Test("vba is my language")

خروجی این حالات هم True میباشه چون خصوصیت IgnoreCase (حساسیت به حروف کوچک و بزرگ) رو در حالت True قرار دادیم
ناگفته نمونه قواعد RegExp ممکنه در همه زبان یکی نباشه . مثلاً در php برای حساس نبودن به حروف کوچک و بزرگ در آخر عبارت پترن حرف i بعد از اسلش دوم اضافه می کنیم

در مثال بعد بجای متن vba is my language متن absvbanbk جایگزین می کنیم ,
در اینجا با همون قاعده پترن قبلی میخوایم در متن absvbanbk جستجو بشه که آیا در این متن سه حرف پشت سرهم vba وجود داره یه نه
در اینجا هم اگه خروجی بگیریم نتیجه True میباشه ، چون تطبیق وجود داره
در همین مثال اگه بجای حروف از عدد هم استفاده کنین فرقی نداره همون قاعده برای متن عددی هم برقراره

eb_1345
چهارشنبه 02 آبان 1403, 14:11 عصر
در مثال این پست میخوایم از علامت یا نماد dot یا نقطه . در عبارت پترن استفاده کنیم
متن یا رشته رو معادل همون متن قبلی vba is my language قرار میدیم و عبارت پترن هم معادل v.b
همونطور که در جدول تصویر پست 3 توضیح داده شده نماد dot یا نقطه به معنی هر کاراکتری میباشه .طبق قاعده پترن این مثال خواسته میشه که در vba is my language جستجو بشه و سه حرف ( در هرجای ) متن پیدا کنه که با v شروع بشه و با a هم ختم بشه وسط این دو حرف هم هرچی میخواد باشه
کدهای مثال جدید:


Dim regx
Set regx = CreateObject("VBScript.RegExp")
regx.Pattern = "v.a"
MsgBox regx.Test("vba is my language")

خروجی مثال جدید هم True میباشه چون چنین کلمه سه حرفی ای که همون حروف vba میباشه در متن وجود داره
بدیهیست در مثال فوق اگه دنبال کلمه 4 حرفی یا بیشتر بودیم و حرف اول و آخرش مشخص بود باید معادل بقیه حرف نقطه بذاریم مثلاً برای جستجوی حروف 4 حرفی از v..a در قاعده پترن باید استفاده کنیم.

در مثال بعدی میخوایم از نماد یا علامت hat یا توان ^ در عبارت پترن استفاده کنیم
در اینجا هم متن یا رشته رو معادل همون متن قبلی vba is my language قرار میدیم و عبارت پترن هم معادل vba ^
همونطور که در جدول تصویر پست 3 توضیح داده شده علامت توان یا ^ به معنی اینه که متن ما با حرف یا کلمه یا جمله ای که در عبارت پترن بعد از علامت توان^ درج شده شروع بشه .


Dim regx
Set regx = CreateObject("VBScript.RegExp")
regx.Pattern = "^vba"
MsgBox regx.Test("vba is my language")

خروجی این مثال هم True میباشه چون طبق قاعده پترن که تعیین کرده ایم بعد علامت توان ^ متن باید با حروف vba شروع بشه می بینیم همینطوری هم هست متن با این سه حرف شروع شده
حالا در همین مثال اگه در عبارت پترن بجای vba^ از vba is^ هم استفاده کنیم باز هم خروجی این مثال True میباشه چون متن ما با vba is^ هم شروع شده

محمد رضا بهبودی
چهارشنبه 02 آبان 1403, 15:28 عصر
با سلام و احترام
استاد گرانقدر
بسیار مطلب آموزنده و خوبی بود
ممنون

eb_1345
چهارشنبه 02 آبان 1403, 16:16 عصر
در مثال این پست از علامت یا نماد Dollar یا $ در عبارت پترن استفاده میکنیم
متن یا رشته رو معادل همون متن قبلی vba is my language قرار میدیم و عبارت پترن هم معادل $language
در جدول تصویر پست 3 اشاره شده که علامت یا نماد Dollar یا $ به معنی نقطه پایان رشته میباشه . قاعده پترن این مثال این است که متن vba is my language با کلمه language خاتمه پیدا کنه
کدهای این مثال:


Dim regx
Set regx = CreateObject("VBScript.RegExp")
regx.Pattern = "language$"
MsgBox regx.Test("vba is my language")

مسلماً مقدار خروجی این مثال true میباشه چون متن فوق مطابق قاعده پترن با کلمه language خاتمه پیدا کرده
مجدداً یادآوری میکنم که در عبارت پترن بجای کلمه میتونه یک حرف یا یک جمله هم قرار بگیره



در مثال بعدی از علامت یا نماد آکولاد باز و بسته یا {} در عبارت پترن استفاده میکنیم
متن یا رشته رو معادل همون متن قبلی vba is my language قرار میدیم و عبارت پترن هم معادل my{1,2}
در عبارت پترن علامت آکولاد باز و بسته یا {} تعداد کاراکتر رو مشخص میکنه . بر اساس قاعده این پترن در متن دو کلمه جستجو میشه که حرف آخر این دو حرف یعنی y یکبار تا دو بار پشت سرهم تکرار شده باشه .
مقدار خروجی در این مثال true میباشه چون حرفy در محدود 1 تا 2 حداقل یکبار پشت سر هم تکرار شده . ما اگه بجای عدد دوم که 2 است هم عدد 1 قرار بدیم هم هر عدد بزرگتر از 2 بازهم مقدار خروجی true میباشه ، چون در تمام این محدوده ها حرف حرفy حداقل یکبار تکرار شده
در همین مثال که در خصوص نماد باز و بسته آکولاد یا {} توضیح داده میشه اگه عدد دوم داخل آکولاد خالی باشه به معنی یا بیشتر میباشه . مثلا در همین مثال اگه عبارت پترن رو بصورت my{1,} قرار بدیم باز مقدار خروجی true میشه
کد های این مثال :


Dim regx
Set regx = CreateObject("VBScript.RegExp")
regx.Pattern = "my{1,2}"
MsgBox regx.Test("vba is my language")

یا


Dim regx
Set regx = CreateObject("VBScript.RegExp")
regx.Pattern = "my{1,}"
MsgBox regx.Test("vba is my language")


در ارتباط با کاربرد نماد باز و بسته آکولاد یا {} در عبارت پترن یک حالت دیگه وجود داره و اون اینکه در داخل آکولاد فقط یک عدد داشته باشم مثل {2} . این حالت برای زمانیست که بخواهیم تکرار حرف آخر کلمه قبل از آکولاد فقط به تعداد عدد داخل آکولاد تکرار بشه

eb_1345
چهارشنبه 02 آبان 1403, 16:37 عصر
دوستان عنایت داشته باشن در مثال هامون فعلاً هر کدوم از نماد ها رو در عبارت پترن بصورت تکی استفاده می کنیم تا بعداً ان شاءالله بصورت ترکیبی از اونها استفاده کنیم . فعلاً برای اینکه قاطی نکنیم بهتره کاربرد هر کدومشونو به تنهائی یاد بگیریم تا یواش یواش ملکه ذهنمون بشه و بعداً اونا رو با همدیگه بکار ببریم

eb_1345
چهارشنبه 02 آبان 1403, 21:58 عصر
از این پست به بعد سعی میکنم مطالب رو تا حد امکان خلاصه و بدون کد نویسی مطرح کنم و توضیحات اضافه و کد نویسی رو موکول میکنم به زمانی که در ارتباط با موضوع مورد ابهام سوالی پرسیده شود .

علامت Plus یا +
اگه در عبارت پترن مثلاً +a بود بدین معنیه که حرف a حداقل یکبار در متن تکرار شده باشه یا مثلا اگه عبارت متن بصورت +abc بود بدین معنیه که اگه در متن کلمه سه کاراکتری abc وجود داشت کاراکتر c اون میتونه یک یا چند بار تکرار بشه . مثلا حروف abc میتونه بصورت abccccc هم در متن باشه و قابل تطبیقه

علامت Star یا ستاره
اگه در عبارت پترن مثلاً *k بود بدین معنیه که حرف k میتونه نباشه یا اگه بود میتونه یک یا چند بار در متن تکرار بشه . بفرض متن ما vba is my language میباشه و می بینیم که حرف k در این متن نیست . در این حالت قاعده پترن درسته و خروجی هم true میباشه. یا مثلاً اگه عبارت پترن بصورت *abc بود معنیش اینه که در متن دو حرف ab باید باشه ولی اگه حرف c وجود هم نداشت مشکلی نیست و اگه هست میتونه یک یا چند بار تکرار بشه . در این حالت قاعده *abc برای ab یا abc یا abccccccccc در متن مطابقت داره

علامت optional یا ?
علامت ؟ به معنی اختیاری بودن می باشه
اگه در عبارت پترن مثلاً ؟k بود بدین معنیه که حرف k میتونه در متن هم باشه هم نباشه . یا مثلاً اگه عبارت پترن بصورت ؟abc بود معنیش اینه که در متن دو حرف ab باید باشه ولی حرف c هم میتونه در کنار این دو حرف هم باشه هم نباشه . در این حالت قاعده ؟abc برای رشته ab یا abc در متن صادقه . یا در عبارت
4?123 شامل عددهای1234 و 124 میشه

eb_1345
پنج شنبه 03 آبان 1403, 13:04 عصر
بنظرم برای استفاده از RegEx یا (Regular Expressions) بمنظور انجام عملیات تطابق و جایگذاری عبارات در رشته ها بواسطه قواعد خاص و استفاده از نمادها و دستورات مخصوصی که در اون وجود داره نیاز به یک آموزش در یک تاپیک مستقل داره


خود ابجکت regexp ساده است و پراپرتی ها و متدهاش خیلی کم و سرراستن.
قسمت سختش نوشتن اون پترن هست که بدبختانه آموزش برنمیداره بس که گسترده است
و خیلی راحت میشه یک پترن بد نوشت که فشار به پردازنده بیاره یا کلا تو لوپ بیفته!


قبل از ایجاد این تاپیک آموزشی در تاپیک : تشخیص فیلدی که داده های آن از چندین space تشکیل شده (https://barnamenevis.org/newreply.php?do=postreply&t=589940) بنده پیشنهاد ایجاد یک تاپیک آموزشی در ارتباط با Regular Expressions نمودم که جناب استاد مازولاق با توجه به تجربه ای که از قبل در این زمینه داشته ان اظهار نمودن که مقوله Regular Expressions بلحاظ گستردگی زیاد پترن های اون آموزش بردار نیست ( نقل قول بالا) ولی بنده بخاطر شناخت و اطلاعات مختصری که در این مورد داشتم تصور کردم برای این موضوع میشه یک تاپیک آموزشی ایجاد کرد . ولی طی این یکی دو روز اخیر کاملاً متوجه گستردگی ای که پترن های این آبجکت داره و استاد بدرستی به اون اشاره داشتن شدم .

mazoolagh
پنج شنبه 03 آبان 1403, 19:03 عصر
قبل از ایجاد این تاپیک آموزشی در تاپیک : تشخیص فیلدی که داده های آن از چندین space تشکیل شده (https://barnamenevis.org/newreply.php?do=postreply&t=589940) بنده پیشنهاد ایجاد یک تاپیک آموزشی در ارتباط با Regular Expressions نمودم که جناب مازولاق با توجه به تجربه ای که از قبل در این زمینه داشته ان اظهار نمودن که مقوله Regular Expressions بلحاظ گستردگی زیاد پترن های اون آموزش بردار نیست ( نقل قول بالا) ولی بنده بخاطر شناخت و اطلاعات مختصری که در این مورد داشتم تصور کردم برای این موضوع میشه یک تاپیک آموزشی ایجاد کرد . ولی طی این یکی دو روز اخیر کاملاً متوجه گستردگی ای که پترن های این آبجکت داره و ایشون بدرستی به اون اشاره داشتن شدم .

من به نوبه خودم بابت این تاپیک مفید تشکر میکنم.
به نظرم مقصود حاصل و نکات کلیدی بسیاری روشن شده.
مواردی که میبایست با صرف وقت زیاد و گشتن در سایت های مختلف (و خواندن متن های گنگ) بهش میرسیدیم،
اینجا با زبانی روان بیان شد که راه رو هموار میکنه.

البته که اموزش امر دائمی و مستمر هست،
در مورد regexp هر مسئله (نه ساده اش) میتونه یک چالش باشه و راستش حتی تجربه پرباری از طراحی پترن هم ممکنه کمکی نکنه!
خوشبختانه برای کارهای روتین و متداول بیشتر پترن ها مشخص و آماده هست و کتابخانه های خوبی از این پترن ها پیدا میشه.
طراحی پترن بخش بیشترش یک هنر هست، شما ممکنه کار با قلم مو و رنگ و چکش و قلم رو به کسی بتونین یاد بدین -
ولی نقاشی و مجسمه سازی رو نمیشه یاد داد مگر این که یک استعدادی هم باشه.

در این یکی دو سال اخیر هم در دسترس بودن هوش ساختگی کمک خیلی خوبی هست.

mazoolagh
پنج شنبه 03 آبان 1403, 19:12 عصر
برای این که این تاپیک سودمند ادامه پیدا کنه،
یک پرسش کاملا عملی رو برای خوانندگان این بحث مطرح میکنم که بتونین میزان یادگیری خودتون رو محک بزنین:

یک پترن برای بررسی درست بودن تاریخ خورشیدی طراحی کنین:
1- فرمت YYYY/MM/DD
2- محدوده مجاز سال از 1300 تا 1499
3- محدوده مجاز ماه از 01 تا 12
4- محدوده مجاز روز ماه از 01 تا 31 برای 6 ماه اول سال
5- محدوده مجاز روز ماه از 01 تا 30 برای ماه از 07 تا 11
6- محدوده مجاز روز ماه از 01 تا 29 برای ماه 12 (کبیسه نداریم)

eb_1345
پنج شنبه 03 آبان 1403, 22:10 عصر
من به نوبه خودم بابت این تاپیک مفید تشکر میکنم.
به نظرم مقصود حاصل و نکات کلیدی بسیاری روشن شده.
مواردی که میبایست با صرف وقت زیاد و گشتن در سایت های مختلف (و خواندن متن های گنگ) بهش میرسیدیم،
اینجا با زبانی روان بیان شد که راه رو هموار میکنه.

البته که اموزش امر دائمی و مستمر هست،
در مورد regexp هر مسئله (نه ساده اش) میتونه یک چالش باشه و راستش حتی تجربه پرباری از طراحی پترن هم ممکنه کمکی نکنه!
خوشبختانه برای کارهای روتین و متداول بیشتر پترن ها مشخص و آماده هست و کتابخانه های خوبی از این پترن ها پیدا میشه.
طراحی پترن بخش بیشترش یک هنر هست، شما ممکنه کار با قلم مو و رنگ و چکش و قلم رو به کسی بتونین یاد بدین -
ولی نقاشی و مجسمه سازی رو نمیشه یاد داد مگر این که یک استعدادی هم باشه.

در این یکی دو سال اخیر هم در دسترس بودن هوش ساختگی کمک خیلی خوبی هست.

سلام و عرض ارادت خدمت دوست و استاد گرامی جناب آقای مازولاق عزیز !
از لطف و محبت همیشگی شما بسیار سپاسگزارم
همونطور که قبلاً هم در یکی از تاپیک ها خدمتتون عرض کردم بنده خیلی حال و حوصله ایجاد تاپیک های آموزشی ندارم . چون واقعاً معتقدم کسی که اقدام به ایجاد یک تاپیک آموزشی می کنه باید از زوایای مختلف اشراف و تسلط بالائی در خصوص موضوع مورد آموزش داشته باشه که بنده شخصاً چنین توانائی و تسلطی بخصوص از نظر تئوری در مباحث آموزشی ندارم .
راستش این تاپیک آموزشی هم که ایجاد کردم مطمئن بودم شما مثل همیشه در کنارمون خواهید بود و عیب و ایرادهای کارمون رو بهمون گوشزد می کنی
و اما در خصوص ادامه مطالب آموزشی این تاپیک ، امروز با خودم فکر کردم که اگه به همین منوال این بحث آموزش رو ادامه بدم و در هر پستی در خصوص دوسه نوع از نمادها مطلب بنویسم و مثال بزنم تعداد پست ها بواسطه گستردگی مطلب خیلی زیاد میشه و در نهایت خیلی از دوستانی که علاقمند به این بحث آموزشی هستن خسته بشن و در نهایت چیزی هم دستگیرشون نشه.از طرفی بفکر روشی بودم که بهتر و راحتتر بشه قواعد کار با آبجکت RegExp رو یاد گرفت.
در همین راستا ایده ای به ذهنم خطور کرد و برای عملی نمودن اون دست بکار شدم . ایده مربوطه این بود که بیائیم این مطالب آموزشی رو با مثال های مختلف و متنوع بصورت یک برنامه در اکسس پیاده سازی کنیم بطوریکه در این برنامه امکان جستجوی علائم با توضیحات و مثال های مربوط به هر علامت یا نماد وجود داشته باشه و در فرمی دیگر هم همزمان امکان تست الگوی RegExp مورد نظرمون فراهم کنیم .
برنامه ضمیمه حاصل تلاش سه چهار ساعته امروز بنده برای عملی نمودن این ایده می باشه .
به نظر خودم خوبی استفاده از این ایده و روش اینه که در فرم اصلی میتوان هر کدوم از علائم مورد استفاده در پترن رو جستجو کرد ، در کادر توضیح علامت با کاربردهای اون علامت آشنائی پیدا کرد و درقسمت سابفرم هم با مشاهده مثال های مختلف (که به مرور به اون اضافه میشه) با کاربرد علامت مربوطه در الگوهای مختلف آشنا شد. در کل بجای جستجوی کاربرد یک علامت در الگوهای RegExp در جاهای مختلف ، از طریق این برنامه میشه موارد مورد نیاز رو در یک صفحه مشاهده کرد.
ان شاءالله با کمک های جنابعالی بتونیم یک رنگ و بوی حرفه ای به این برنامه بدیم که قابل استفاده برای همه دوستان بخصوص علاقمندان به فراگیری مقوله Regular Expressions باشه

moustafa
جمعه 04 آبان 1403, 12:21 عصر
عرض سلام وادب جناب بهرامی عزیز
واقعا دستتون درد نکنه از این همه وقت وانرژی که میذارین و حوصله ای که به خرج میدین . اجرکم عندالله

eb_1345
جمعه 04 آبان 1403, 15:34 عصر
عرض سلام وادب جناب بهرامی عزیز
واقعا دستتون درد نکنه از این همه وقت وانرژی که میذارین و حوصله ای که به خرج میدین . اجرکم عندالله
ممنون !
دوران بازنشستگی رو یجوری باید سر خودمونو گرم کنیم دیگه . :لبخندساده:

eb_1345
شنبه 05 آبان 1403, 14:09 عصر
یک لیست باکس برای نمایش رشته های تطبیق داده شده به نمونه پست 17 اضافه کردم . لطفاً بررسی و اظهار نظر فرمائید!

eb_1345
شنبه 05 آبان 1403, 17:36 عصر
ان شاءالله نمونه پترن های مربوط به هر علامت به مرور به برنامه اضافه میشه !

eb_1345
شنبه 05 آبان 1403, 18:37 عصر
اگه بشه میخوام در برنامه نمونه این خاصیت رو به فرم تست الگو اضافه کنم که وقتی عبارتی در الگو وارد میشه علاوه بر اعلام مطابقت داشتن یا نداشتن عبارت الگو و نمایش رشته در لیست باکس پائین فرم ، عبارت مربوطه رو در متن سلکت و هایلایت کنه . این خصوصیت برای جستجو در متن های طولانی خیلی بدرد بخوره
شرط ابتدائی اجرای چنین حالتی اینه که خصوصیت Text Format تکست باکس متن در حالت Rich Text باشه
156258

محمد رضا بهبودی
یک شنبه 06 آبان 1403, 15:02 عصر
با سلام و احترام
استاد گرامی
ابتدا ممنون از آموزش بسیار مفید شما
در صورت امکان در پست قبلی تعداد پیدا شده از الگو را هم در قالب یک پیغام و یا تکست باکس نمایش دهد
به طور مثال 25 حرف a پیدا شده است

eb_1345
یک شنبه 06 آبان 1403, 23:57 عصر
با سلام و احترام
استاد گرامی
ابتدا ممنون از آموزش بسیار مفید شما
در صورت امکان در پست قبلی تعداد پیدا شده از الگو را هم در قالب یک پیغام و یا تکست باکس نمایش دهد
به طور مثال 25 حرف a پیدا شده است
سلام جناب آقای بهبودی عزیز!
وقت بخیر!
ممنون از لطف شما
راستش این مورد رو در فرم تست الگو نمونه برنامه امتحان کردم ولی نتیجه مطلوبی حاصل نشد .
ان شاءالله بعدا سر فرصت روش بیشتر کار می کنم . حالا این مورد خیلی هم مهم نیست، الآن مورد مهمتر موضوع تاپیکه که باید بهش بپردازیم.

eb_1345
دوشنبه 07 آبان 1403, 07:59 صبح
برای این که این تاپیک سودمند ادامه پیدا کنه،
یک پرسش کاملا عملی رو برای خوانندگان این بحث مطرح میکنم که بتونین میزان یادگیری خودتون رو محک بزنین:

یک پترن برای بررسی درست بودن تاریخ خورشیدی طراحی کنین:
1- فرمت YYYY/MM/DD
2- محدوده مجاز سال از 1300 تا 1499
3- محدوده مجاز ماه از 01 تا 12
4- محدوده مجاز روز ماه از 01 تا 31 برای 6 ماه اول سال
5- محدوده مجاز روز ماه از 01 تا 30 برای ماه از 07 تا 11
6- محدوده مجاز روز ماه از 01 تا 29 برای ماه 12 (کبیسه نداریم)

با توجه به اینکه در پستهای قبل و در برنامه نمونه به اندازه کافی در مورد علائم و نمادهای مورد استفاده در الگوها توضیح داده شده بهتر است از این پست به بعد به مسائل و حل اونها بپردازیم
فعلاً تاریخ رو از سال 1100 بدون در نظر گرفتن محدوده ها ی فوق بررسی و چک می کنیم تا در صورت درست بودن در پست های بعد محدوده هائی که جناب آقای مازولاق اشاره کرده ان در اون لحاظ کنیم

تابعی بصورت زیر برای اعتبار سنجی تاریخ ایجاد می کنیم و عبارت الگو رو در اون قرار میدیم:

Public Function ShamsiDateValid(StrDate As String) As Boolean
On Error GoTo Error_Handler
Dim regx
Set regx = CreateObject("VBScript.RegExp")
regx.Pattern = "^(\d{4}\/?(0[1-9]|1[012])\/?(0[1-9]|[12][0-9]|3[01]))"
ShamsiDateValid = regx.Test(StrDate)
If ShamsiDateValid = False Then
MsgBox "! فرمت تاريخ اشتباه وارد شده ", vbOKOnly + vbInformation + vbMsgBoxRight, "توجه!"
'TxtDate.SetFocus
End If
Error_Handler:
If Err.Number > 0 Then MsgBox " خطاي :" & Err.Number & " " & Err.Description
End Function


در نهایت بصورت زیر تابع فوق برای مثلا تاریخ ورودی 1401/08/07 فراخوانی می کنیم:


Call ShamsiDateValid("1401/08/07")

من در حل سوال توضیح خاصی ندادم چون اگه اینکار رو میکردم چندین خط باید توضیح میدادم و برام خسته کننده بود.

eb_1345
دوشنبه 07 آبان 1403, 11:57 صبح
دوستان خاطرشون مونده که کاربرد علامت ? درعبارت الگو چی بود ؟
همونطور که در پست 13 اشاره کردیم علامت ? به معنی اختیاری بودن کاراکتر قبل از خودشه . یعنی اگه یک رشته ورودی بصورت ?/1200 داشته باشیم علامت اسلش / میتونه هم باشه هم نباشه . پس بیائیم با توجه به قاعده این علامت اون رو در عبارت الگوی تاریخ بعد از اسلش اضافه کنیم تا اگر کاربر در هنگام ورود تاریخ از اسلش / هم استفاده نکرد پیغامی صادر نشه.
با اضافه نمودن علامت فوق عبارت الگو بصورت زیر تبدیل میشه:


regx.Pattern = "^(\d{4}\/?(0[1-9]|1[012])\/?(0[1-9]|[12][0-9]|3[01]))"

mazoolagh
دوشنبه 07 آبان 1403, 19:12 عصر
سلام دوباره خدمت جناب بهرامی

1- پریروز میخواستم براتون چیزی بفرستم، پیام خصوصی شما بسته بود (من هم بستم البته)،
لطفا یک ایمیل به همین آیدی در یاهو بزنین.

2- این پترن های پست های 25 و 26 بنظرم درست کار نکنه.

3- یک اشتباه تایپی هم در نامیدن / به بک اسلش هست - که اسلش درسته.

4- برای سال از این استفاده کنین (طبق خواسته):

(13\d\d|14\d\d)

5- برای ماه هم باید captured group باشه (که بعدا بتونیم مقدار ماه رو بخونیم - مثل سال در مورد 4)
ولی داخل خودش چند non-captured group نیاز هست برای alternativeها (6 ماه اول و دوم)
که هر کدوم اینها باز برای روز چند آلترناتیو captured group میخواد.

eb_1345
دوشنبه 07 آبان 1403, 20:49 عصر
برای این که این تاپیک سودمند ادامه پیدا کنه،
یک پرسش کاملا عملی رو برای خوانندگان این بحث مطرح میکنم که بتونین میزان یادگیری خودتون رو محک بزنین:

یک پترن برای بررسی درست بودن تاریخ خورشیدی طراحی کنین:
1- فرمت YYYY/MM/DD
2- محدوده مجاز سال از 1300 تا 1499
3- محدوده مجاز ماه از 01 تا 12
4- محدوده مجاز روز ماه از 01 تا 31 برای 6 ماه اول سال
5- محدوده مجاز روز ماه از 01 تا 30 برای ماه از 07 تا 11
6- محدوده مجاز روز ماه از 01 تا 29 برای ماه 12 (کبیسه نداریم)



regx.Pattern = "((13|14)([0-9]{2})/?(((0[1-6])/?((0[1-9])|([12][0-9])|(3[0-1])))|(((0[7-9])|(1[0-1]))/?((0[1-9])|([12][0-9])|(30)))|((12)/?((0[1-9])|([12][0-9])|(29)))))"

eb_1345
دوشنبه 07 آبان 1403, 21:19 عصر
سلام دوباره خدمت جناب بهرامی
1- پریروز میخواستم براتون چیزی بفرستم، پیام خصوصی شما بسته بود (من هم بستم البته)،
لطفا یک ایمیل به همین آیدی در یاهو بزنین.

با عرض سلام متقابل
دریافت پیام خصوصی رو فعال کردم

eb_1345
دوشنبه 07 آبان 1403, 22:10 عصر
گاهاً در حال تایپ مطلب در پست جدید هستم که کاری برایم پیش میاد که ممکنه انجامش طول بکشه در این حالت صفحه رو همچنان باز میذارم تا بعداً اون رو ارسال کنم ، در همین زمان ممکنه قبل از ارسال پست شخص دیگری پستی رو ارسال کنه که من متوجه اون نمیشم . مثل پست شماره 27 که در ساعت 1403, 19:12 عصر توسط جناب استاد مازولاق ارسال شده در حالیکه در همین زمان من مشغول انجام کار دیگه بودم و بدون اینکه چک کنم که پست جدیدی ارسال شده یا نه پست خودم رو که زمان ارسالش 1403, 20:49 عصر بوده ارسال نمودم.
این مطلب رو عرض کردم که یموقع خدای نکرده سوء تفاهمی ایجاد نشه

eb_1345
سه شنبه 08 آبان 1403, 12:05 عصر
نکته :
همانطور که در مطالب پست های قبل اشاره کردیم برخی از کاراکتر ها در عبارت الگو (پترن) معنی خاصی می دهن و اگه بخواهیم خود اون کاراکترها رو در متن انتخاب نمائیم در عبارت الگو قبل از اون کاراکتر باید یک بک اسلش \ قرار بدهیم
مثلاً میخواهیم تمام نقطه هائی که در متن میباشه انتخاب بشه باید در عبارت الگو اون رو بصورت .\ قرار بدهیم یا میخواهیم تمام بک اسلش هائی که در متن وجود داره انتخاب بشه در عبارت الگو باید اون رو بصورت \\ قرار بدهیم . برای بقیه کاراکترها به همین صورت عمل می کنیم

?\
*\
^\
&\
[\
]\
{\
}\
)\
(\

mazoolagh
سه شنبه 08 آبان 1403, 13:04 عصر
regx.Pattern = "((13|14)([0-9]{2})/?(((0[1-6])/?((0[1-9])|([12][0-9])|(3[0-1])))|(((0[7-9])|(1[0-1]))/?((0[1-9])|([12][0-9])|(30)))|((12)/?((0[1-9])|([12][0-9])|(29)))))"


چند نمونه رو تست کردم و بنظر درست میومد،
و این که / رو optional کردین هم بسیار خوب بود.
واقعا دست مریزاد.

اما:
/ اگر optional هست کلا باید اینجوری باشه،
این پترن در وضعیت فعلی هر کدوم رو به تنهایی درنظر میگیره:
یعنی 1383/1122 , 138311/22 هم valid هستن.

مورد بعدی این که الان کلا یک captured group داریم که کل تاریخ رو برمیگردونه،
و این خودش درونش 24 تا captured group دیگه داره.
در حالی که باید کلا 3 تا باشه : بخش سال و بخش ماه و بخش روز.

mazoolagh
سه شنبه 08 آبان 1403, 13:10 عصر
در صورت امکان در پست قبلی تعداد پیدا شده از الگو را هم در قالب یک پیغام و یا تکست باکس نمایش دهد
به طور مثال 25 حرف a پیدا شده است

سلام و روز خوش
شما باید match collection بسازین و length اون رو بخونین.
یک کد نمونه در پست شماره 39 تاپیک زیر هست:
تشخیص فیلدی که داده های آن از چندین space تشکیل شده (https://barnamenevis.org/showthread.php?589940-%D8%AA%D8%B4%D8%AE%DB%8C%D8%B5-%D9%81%DB%8C%D9%84%D8%AF%DB%8C-%DA%A9%D9%87-%D8%AF%D8%A7%D8%AF%D9%87-%D9%87%D8%A7%DB%8C-%D8%A2%D9%86-%D8%A7%D8%B2-%DA%86%D9%86%D8%AF%DB%8C%D9%86-space-%D8%AA%D8%B4%DA%A9%DB%8C%D9%84-%D8%B4%D8%AF%D9%87)

eb_1345
سه شنبه 08 آبان 1403, 14:50 عصر
سلام و روز خوش
شما باید match collection بسازین و length اون رو بخونین.
یک کد نمونه در پست شماره 39 تاپیک زیر هست:
تشخیص فیلدی که داده های آن از چندین space تشکیل شده (https://barnamenevis.org/showthread.php?589940-%D8%AA%D8%B4%D8%AE%DB%8C%D8%B5-%D9%81%DB%8C%D9%84%D8%AF%DB%8C-%DA%A9%D9%87-%D8%AF%D8%A7%D8%AF%D9%87-%D9%87%D8%A7%DB%8C-%D8%A2%D9%86-%D8%A7%D8%B2-%DA%86%D9%86%D8%AF%DB%8C%D9%86-space-%D8%AA%D8%B4%DA%A9%DB%8C%D9%84-%D8%B4%D8%AF%D9%87)
سلام و روز شما هم خوش!
لطفا در مورد فانکشن زیر که برای این مورد ایجاد کرده ام اظهار نظر فرمائین !



Public Function RegExCountWords(ByVal Text As String, PatternText As String) As Long
Dim regx
Set regx = CreateObject("VBScript.RegExp")
regx.Pattern = PatternText
regx.Global = True
RegExCountWords = regx.Execute(Text).Count
End Function

در مثال زیر با استفاده از تابع فوق میتوان تعداد کاراکتر o در رشته ورودی رو بدست آورد .


MsgBox RegExCountWords("www.bahrami125@yahoo.com", "o")

یا در مثال زیر تعداد کلمه در در رشته ورودی که فارسی هم هست بدست میاد:


MsgBox RegExCountWords("در مثال زير با استفاده از تابع فوق ميتوان تعداد کاراکتر o در رشته ورودي رو بدست آورد .", "در")

فکر کنم در متن های چند خطی رشته فارسی هم کاربرد داشته باشه .

البته در متن انگلیسی برای بدست آوردن تعداد یک کاراکتر یا کلمه خاص در یک متن میتونیم با ترکیب تابع Len و Replace اینکار رو انجام بدهیم
مثلاً در همون مثال اولی میخواهم تعداد حرف o رو در متن www.bahrami125@yahoo.com ورودی بدست بیاریم. برای اینکار تابع رو بصورت زیر مینویسیم :


CountWords = Len("www.bahrami125@yahoo.com") - Len(Replace("www.bahrami125@yahoo.com", "o", ""))

ولی با این کد تعداد در متون فارسی درست بدست نمیاد ولی تابعی که در اون از پترن استفاده شده برای بدست آوردن تعداد در هر دو حالت فارسی و انگلیسی مناسبتره

eb_1345
چهارشنبه 09 آبان 1403, 18:23 عصر
اما:
/ اگر optional هست کلا باید اینجوری باشه،
این پترن در وضعیت فعلی هر کدوم رو به تنهایی درنظر میگیره:
یعنی 1383/1122 , 138311/22 هم valid هستن.

مورد بعدی این که الان کلا یک captured group داریم که کل تاریخ رو برمیگردونه،
و این خودش درونش 24 تا captured group دیگه داره.
در حالی که باید کلا 3 تا باشه : بخش سال و بخش ماه و بخش روز.
با سلام دویاره
برای رفع ایراد مورد درج علامت اسلش / یا نادیده گرفتن اون در رشته تاریخ میشه در تابع ShamsiDateValid با چک کردن تعداد اسلش ها تاریخ رو اعتبار سنجی کرد
تعداد گروه ها هم به 6 تا تقلیل پیدا کرد.البته عبارت قسمت سال بدون گروه هم مینونه باشه ولی بنظرم برای خوانائی بیشتر پترن باشه بهتره


Public Function ShamsiDateValid(Strdate As String) As Boolean
On Error GoTo Error_Handler
Dim regx
Dim Strdt As String
regx.Pattern = "(13|14[0-9]{2}/?)(0[1-6]/?(0[1-9]|[12][0-9]|3[0-1])|(0[7-9]|1[0-1])/?(0[1-9]|[12][0-9]|30)|12/?(0[1-9]|[12][0-9]))"
ShamsiDateValid = regx.Test(Strdate)
If ShamsiDateValid = False Then
MsgBox "! فرمت تاريخ اشتباه وارد شده ", vbOKOnly + vbInformation + vbMsgBoxRight, "توجه!"
Exit Function
ElseIf ShamsiDateValid = True Then
If Len(Strdate) - Len(Replace(Strdate, "/", "")) = 1 Then
MsgBox "! فرمت تاريخ اشتباه وارد شده ", vbOKOnly + vbInformation + vbMsgBoxRight, "توجه!"
Exit Function
End If
End If
Error_Handler:
If Err.Number > 0 Then MsgBox " خطاي :" & Err.Number & " " & Err.Description
End Function

eb_1345
چهارشنبه 09 آبان 1403, 19:25 عصر
نسخه آپدیت شده نمونه برنامه پست 17 در ضمیمه این پست قرار داده شد.
ضمن اضافه کردن مختصر اطلاعاتی به جدول برنامه نسخه آپدیت شده به لیست باکس مربوط به رشته های تطبیق شده در فرم تست عبارت پترن یا الگو ستون دیگه ای تحت عنوان موقعیت اضافه کردم . در این ستون موقعیت هر رشته تطبیق شده از نقطه شروع تا نقطه پایان اون مشخص میشه به عبارتی دیگه برای هر تطبیق مشخص میشه که از چندمین کاراکتر شروع و در چندمین کاراکتر خاتمه پیدا کرده

eb_1345
پنج شنبه 10 آبان 1403, 12:57 عصر
در پست شماره 16 جناب آقای مازولاق یک پرسش کاربردی در خصوص اعتبار سنجی تاریخ شمسی (مطابق موارد مورد اشاره در پست فوق) از طریق RegEx مطرح کردن ،در این پست میخواهم به ارزیابی و اعتبار سنجی شماره موبایل (شماره های مختص کشور ایران)با عبارت‌های با قاعده(Regex) بپردازیم
ساده ترین پترن برای اعتبار سنجی شماره موبایل بدون کد کشور بصورت زیر میباشه :


^0?9\d{9}$


این پترن به ما میگه که اولاً شماره موبایل وارده باید با 09 شروع شود اما بکار بردن 0 اختیاری می باشه دوماً کاراکترهای وارده بعدی (بعد از 9) باید از نوع عدد و تعداد ارقامش هم 9 تا باشه
در پترن بعدی کد کشور هم لحاظ میشه ولی بصورت اختیاری . یعنی اگه کد کشور هم اضافه نشد شماره وارده اعتبار داره:


^(98)?0?9\d{9}$

در پترن بالا کد کشور ایران که 98 میباشه بصورت گروه تعیین کرده ایم و اون رو بصورت آپشنال قرار داده ایم یعنی با گذاشتن علامت ? بعد از گروه اضافه نمودن اون رو بصورت اختیاری درآورده ایم
بنابراین با آبشنال (Optional) قرار دادن هم 0 و هم کد کشور(98) درج اونها اختیاریه
در پترن بعدی علاوه بر درج 0 و کد کشور قبل از شماره موبایل علامت + هم که معمولاً قبل از کد کشور بکار میره اضافه شده ولی بصورت آبشنال یا اختیاری


^\+?(98)?0?9\d{9}$

تابع اعتبار سنجی شماره موبایل:


Public Function MobailNoValid(StrMobailNo As String) As Boolean
On Error GoTo Error_Handler
Dim regx
Dim Strdt As String
Set regx = CreateObject("VBScript.RegExp")
regx.Pattern = "^\+?(98)?0?9\d{9}$"
MobailNoValid = regx.Test(StrMobailNo)
If MobailNoValid = False Then
MsgBox "! شماره موبايل وارده اشتباه مي باشد ", vbOKOnly + vbInformation + vbMsgBoxRight, "توجه!"

Exit Function
End If
Error_Handler:
If Err.Number > 0 Then MsgBox " خطاي :" & Err.Number & " " & Err.Description
End Function

eb_1345
پنج شنبه 10 آبان 1403, 13:09 عصر
انصافاً اگه در حالت معمول و بدون استفاده از پترن بخواهیم بطور مثال شماره موبایل رو در یک رشته اعتبار سنجی کنیم چند خط کد باید بنویسیم و چقدر زمان صرف کد نویسی کنیم ؟ آیا با نوشتن یک پترن در حد یک خط در کار کد نویسی های چند خطی و زمان کد نویسی اون صرفه جوئی نمیشه ؟

mazoolagh
پنج شنبه 10 آبان 1403, 13:33 عصر
با سلام دویاره
برای رفع ایراد مورد درج علامت اسلش / یا نادیده گرفتن اون در رشته تاریخ میشه در تابع ShamsiDateValid با چک کردن تعداد اسلش ها تاریخ رو اعتبار سنجی کرد
تعداد گروه ها هم به 6 تا تقلیل پیدا کرد.البته عبارت قسمت سال بدون گروه هم مینونه باشه ولی بنظرم برای خوانائی بیشتر پترن باشه بهتره

سلام دوباره و روز خوش
اگر بخواهیم فقط با یک پترن چک کنیم گاهی پترن خیلی پیچیده میشه،
که هم نوشتنش سخت و زمانبر هست و هم نگهداریش و دیباگ کردنش.

ولی اگر از کد هم کمک بگیریم میونیم پروسه رو به بخش های کوچک تری بشکنیم
که هم نوشتنش و هم نگهداری و دیباگش ساده تر است ،
و چه بسا پرفورمنس بهتری هم داشته باشه.

در همین مثال میتونیم اول ساختار کلی تاریخ رو درست بودنش رو بررسی کنیم،
و بعد اگر ساختار درست بود ادامه بدیم.

majid44
شنبه 12 آبان 1403, 07:49 صبح
با سلام واحترام ، مطالب بسیار مهم و مفیدی از طرف اساتید محترم مطرح شد از زحمات همگی صمیمانه تشکر وقدر دانی می کنم

eb_1345
شنبه 12 آبان 1403, 14:25 عصر
دستتون درد نکنه جناب بهرامی عزیز
در متون فارسی هم کارایی داره ؟


خواهش میکنم
امتحان نکرده ام ولی به احتمال زیاد در متون فارسی هم باید کارائی داشته باشه .
ان شاء الله به کمک دوستان در ادامه مثال ها و نمونه های کاربردی اعم از فارسی یا انگلیسی رو بررسی و امتحان می کنیم

دوستان ! در ارتباط با متون فارسی بنده چند موردی رو امتحان کردم خوشبختانه جواب داد. البته ناگفته نمونه ممکنه بعضی از علامت هائی که در عبارت الگو بکار برده میشه برای متون فارسی کاربرد نداشته باشه .
در هر حال اگه مواردی مورد نظرتونه امتحان کنین و اگه هم به نتیجه نرسیدین مورد رو در اینجا مطرح کنین تا بررسی بشه .

eb_1345
شنبه 12 آبان 1403, 15:26 عصر
استفاده از Regex Tester
برای تست کردن درستی regex های خودتون میتونین از سایت‌های آنلاین هم استفاده کنین. برای این منظور میتونین از دو سایت های مهم در لینک های زیر استفاده کنین :

1 سایت: regexr.com (https://regexr.com/3cr6f)

2- سایت: regex101.com (https://regex101.com/)

eb_1345
شنبه 12 آبان 1403, 16:21 عصر
یک نکته ظریف در خصوص استفاده از علامت براکت []
در قواعد ریجکس علامت [] بعتوان مجموعه ای از کاراکترها شناخته میشه. یک مثال میزنم که موضوع روشنتر بشه .
فرض می کنیم یک متنی انگلیسی داریم و میخواهیم بفهمیم که آیا در این متن کلمه مثلاً book وجود داره یا نه . برای اینکار باید این کلمه رو مستقیماً در قسمت عبارت پترن وارد می کنیم تا نتیجه جستجو درست باشه و اگه همین کلمه رو داخل علامت [] بذاریم نتیجه درست حاصل نمیشه چرا که وقتی کلمه در داخل براکت قرار گرفت اون کلمه بعنوان یک مجموعه از کاراکترها مورد بررسی و جستجو قرار میگیره ، بعبارتی هر کدوم از کاراکترها بصورت جدا جدا جستجو میشه .مثلاً ممکنه در متن 50 حرف b یا 30 تا حرف o یا 20 تا حرف k در متن باشه و در نتیجه جستجو همه اونها انتخاب میشن ولی اگه book بدون استفاده از علامت براکت و مستقیماً در عبارت الگو یا پترن درج بشه در متن خود کلمه book جستجو میشه نه کاراکترهای اون بصورت جداجدا
برای متن فارسی هم به همین صورته
برای اثبات این موضوع از همون فرم تست الگو نمونه پست 36 استفاده کنین ! در تکست باکس متن جمله من بهرامی هستم رو وارد کنید و در تکست باکس پترن هم بهرامی ، بعد بر روی باتن تست الگو کلیک کنین ! مشاهده خواهین کرد که در لیست باکس پائین یک مورد تطبیق با رشته بهرامی نمایش داده میشه ؛ حالا بهرامی رو داخل براکت درج کنین و امتحان کنین ! در این حالت 9 مورد کارکتر ب ه ر ا م ی در لیست باکس نمایش داده میشه

mazoolagh
شنبه 12 آبان 1403, 18:11 عصر
با اجازه استارتر محترم تاپیک و در تکمیل بخش مربوط به پترن تاریخ خورشیدی:

1- یک پترن کامل همراه با کد در پست شماره 35 جناب آقای بهرامی هست.
2- موردی هم هست که در پست شماره 39 اشاره کردم:

اگر بخواهیم فقط با یک پترن چک کنیم گاهی پترن خیلی پیچیده میشه،
که هم نوشتنش سخت و زمانبر هست و هم نگهداریش و دیباگ کردنش.

ولی اگر از کد هم کمک بگیریم میونیم پروسه رو به بخش های کوچک تری بشکنیم
که هم نوشتنش و هم نگهداری و دیباگش ساده تر است ،
و چه بسا پرفورمنس بهتری هم داشته باشه.

الزاما و در همه جا سپردن همه چیز به regex با پترن های پیچیده،
نتیجه بهتری از کدهای ساده تر بدست نمیده و گاهی اصلا نباید از regex استفاده کرد (این رو در پست جدا بیشتر باز میکنم).

در موردن validation تاریخ خورشیدی،
یک بخش خود ساختار تاریخ هست که با regex بتنهایی شدنی هست (پست شماره 35).

ولی اگر نیاز به اطلاعات تکمیلی باشه مانند دلیل خطا،
باید کد بیشتری استفاده کنیم.

ابتدا یک udt به اسم PersianDate تعریف کردیم که تابع مورد نظر ما CheckPersianDate از همین جنس خواهد بود:
Public Type PersianDate
YYYY As Long
MM As Long
DD As Long
IsValid As Boolean
ErrorText As String
End Type

در اینجا بجای یک پترن پیچیده از 2 پترن ساده استفاده کردیم؛
یکی برای حالتی که تاریخ فرمت نشده هست (بدون /) ،
و یکی برای حالتی که از / برای جدا کردن بخش های تاریخ استفاده شده.

در خود تابع این امکان رو فراهم کردیم که کاربر مشخص کنه تاریخ فرمت شده هست یا نه،
و همچنین حالتی که مشخص نیست (don't care) - یعنی تشخیص شکل تاریخ رو به عهده تابع میگذاریم و با هر کدوم که بخونه کار میکنه.
Public Enum IsDelimited
DontCare = 0
YES = 1
NO = 2
End Enum

Const DateNotDelimited As String = "^(\d{4})(\d{2})(\d{2})$"
Const DateDelimited As String = "^(\d{4})/(\d{2})/(\d{2})$"

Public Function CheckPersianDate( _
DateStr As String, _
Optional Is_Delimited As IsDelimited = IsDelimited.DontCare) _
As PersianDate

Dim mc As MatchCollection

With New RegExp
Select Case Is_Delimited
Case IsDelimited.NO
.Pattern = DateNotDelimited
Case IsDelimited.YES
.Pattern = DateDelimited
Case Else ' dont care
.Pattern = DateDelimited
If Not .test(DateStr) Then
.Pattern = DateNotDelimited
End If
End Select
Set mc = .Execute(DateStr)
End With

With CheckPersianDate
If mc.Count = 0 Then
.YYYY = -1
.MM = -1
.DD = -1
.IsValid = False
.ErrorText = "Invalid PersianDate Structure"
Exit Function
End If

Dim m As Match
Set m = mc(0)
.IsValid = True
.YYYY = m.SubMatches(0)
.MM = m.SubMatches(1)
.DD = m.SubMatches(2)
If .YYYY < 1300 Or .YYYY > 1499 Then
.IsValid = False
.ErrorText = "Invalid Year"
Else
Select Case .MM
Case 1 To 6
If .DD < 1 Or .DD > 31 Then
.IsValid = False
.ErrorText = "Invalid Day of Month"
End If
Case 7 To 11
If .DD < 1 Or .DD > 30 Then
.IsValid = False
.ErrorText = "Invalid Day of Month"
End If
Case 12
If .DD < 1 Or .DD > 29 Then
.IsValid = False
.ErrorText = "Invalid Day of Month"
End If
Case Else
.IsValid = False
.ErrorText = "Invalid Month"
End Select
End If
End With
End Function

mazoolagh
شنبه 12 آبان 1403, 18:33 عصر
با اجازه استارتر محترم تاپیک؛

البته validation کد ملی بدون regex هم کار ساده ای هست.
ولی اینجا برای نشون دادن یک کاربرد ویژه، اون رو در این مسئله به کار میبریم.

Public Function IsValidSSID(SSID As Variant) As Boolean
Select Case VarType(SSID)
Case vbString, vbInteger, vbLong, vbDecimal, vbLongLong, vbSingle, vbDouble
SSID = Format(SSID, String(10, "0"))
Case Else
IsValidSSID = False
Exit Function
End Select

With New RegExp
.Pattern = "(.)\1{9}"
If .test(SSID) Then
IsValidSSID = False
Exit Function
End If
End With

Dim Digits As MatchCollection
With New RegExp
.Pattern = "\d"
.Global = True
Set Digits = .Execute(SSID)
End With
Dim i As Integer
Dim sum As Integer
For i = 0 To 8
sum = sum + Digits(i) * (10 - i)
Next
Dim r As Integer
r = sum Mod 11
If r < 2 Then
IsValidSSID = (Digits(9) = r)
Else
IsValidSSID = (Digits(9) = 11 - r)
End If
End Function

1- نوع پارامتر کد ملی SSID به string محدود نشده،
و اجازه میدیم کاربر هر مقداری رو بتونه به تابع پاس بده،
ولی در تابع فقط مقادیر string و عددی رو قبول میکنیم.

2- با یک تابع format ، سمت چپ string تا 10 کاراکتر رو با 0 پر میکنیم.

3- موضوع کاربردی اینجاست:
.Pattern = "(.)\1{9}"
در واقع الگوریتم انتخاب شده برای کد ملی یک اشکال داره و اونهم این هست که
اگر همه ارقام یکی باشن معتبر نشون میده (البته در واقعیت ما باید از یک api استفاده کنیم و کد ملی رو با سایر مشخصات تطبیق بدیم).
در این پترن از چیزی استفاده کردیم که بهش back-reference میگن و برای capture group شماره 1 - 9 بار تکرار میشه (جمعا 10 بار).
وگرنه میبایست همه این 10 حالت رو چک میکردیم - حالا یا یکی یکی یا در یک حلقه.

4- به جای چرخیدن در SSID و خوندن هر رقم با $mid ، مستقیما از match collection ارقام رو میخونیم و محاسبه میکنیم.

eb_1345
شنبه 12 آبان 1403, 18:53 عصر
با اجازه استارتر محترم تاپیک؛


اجازه ماهم دست شماست استاد گرامی !
ممنون از همراهی و ارائه مطالب مفیدتون !

mazoolagh
شنبه 12 آبان 1403, 19:20 عصر
الزاما و در همه جا سپردن همه چیز به regex با پترن های پیچیده،
نتیجه بهتری از کدهای ساده تر بدست نمیده و گاهی اصلا نباید از regex استفاده کرد (این رو در پست جدا بیشتر باز میکنم).

1- وقتی که string ما ساختار پیچیده و تو در تو داره - مثل html , xml , json یا کدهای برنامه نویسی و .. ،
برای این چیزها باید از parser استفاده کرد که همه اینها دارن و نیازی نیست که دوباره چرخ رو اختراع کنیم.
یک تاپیک دقیقا مربوط به همین وضعیت هست که میتونین با خوندن اون پی ببرین چجوری استفاده از regex بجای parser شما رو به بیراهه میبره:
یک لیست از کلمات کلیدی و تشخیص آن در متن (https://barnamenevis.org/showthread.php?571657)
و روش درست انجام کار با parser مناسب:
تفسیر و آنالیز سورس SQL با استفاده از Microsoft.SqlServer.TransactSql.ScriptDom (https://barnamenevis.org/showthread.php?572300)

2- وقتی stringهای بزرگ،
یا لوپ های سنگین داریم، regex میتونه به شدت روی پرفورمنس تاثیر بگذاره.

3- وقتی با دیتا انکود شده یا غیر text (binary) سروکار داریم.

4- وقتی ساختار دیتا خیلی گسترده باشه، چون نوشتن و خوانایی و نگهداری برنامه سخت و پیچیده میشه.

یک مورد دیگه هم هست که از نظر امنیتی اهمیت پیدا میکنه:
و اون وضعیتی هست که یک پترن بد یا ورودی بد داریم
جوری که انجین regex در لوپ گرفتار میشه و با مصرف منابع سیستم (cpu/ram) سیستم رو دچار overload میکنه که نهایتا ممکنه crash کنه.
برای همین همیشه باید هم اندازه دیتا ورودی رو محدود کرد و هم یک بررسی اولیه روی اون انجام داد.
برای اطلاعات بیشتر جستجو کنین: ReDoS (Regular Expression Denial of Service)

mazoolagh
شنبه 12 آبان 1403, 19:24 عصر
اجازه ماهم دست شماست گرامی !
ممنون از همراهی و ارائه مطالب مفیدتون !

شرمنده که تاپیک از آموزش به گفتگو تبدیل شد،
ولی چراغی که شما روشن کردین به یکی از انگشت شمار تاپیک های با موضوع نو و به انجام رسیده اینجا تبدیل شد.

eb_1345
شنبه 12 آبان 1403, 23:57 عصر
یکی دیگه از کاربردهای RegEx انجام عمل جایگزین کردن یا Replace می باشه . همانطور که میدونیم در متد Replace در یک رشته یا متن یک مقدار مشخص جایگزین مقدار مشخص دیگه ای میشه که با استفاده از تابع Replace اینکار انجام میشه
در مثال زیر فرض می کنیم در عبارتی که در Text1 وارد شده میخواهیم بجای کلمه loop از کلمه lop استفاده کنیم
Text1 = "yadda yadda {1:NM:=12.000:0.120} omtty doom loop{1:NM:=6/6} loppy loop"


از تابع Replace بصورت زیر استفاده می کنیم :


Text1 = Replace(Text1, "loop", "lop")

حالا اگه در متن فوق قرار باشه بجای علامت آکولاد باز وبسته {} و هر کاراکتری که در درون اون وجود داره یک مقدار خاصی جایگزین کنیم آیا در این حالت هم براحتی میشه از تابع Replace بصورت معمولی استفاده کرد؟

در مثال بالا عمل جایگزینی در سرتاسر متن انجام میشه یعنی در سرتاسر متن کلمه lop جایگزین کلمه loop میشه .اما ما میخواهیم مقدار جدید جایگزین علامت آکولاد باز وبسته {} و هر کاراکتری که در درون اون قرار داره بشه . در داخل علامت آکولاد باز وبسته {} ممکنه هر کاراکتری و به هر تعدادی وجود داشته باشه.
بی تردید در این حالت که عمل جایگزینی باید براساس قواعد و شرط و شروط خاصی صورت میگیره لازمه از RegEx کمک گرفته بشه و برای انجام اینکار یک پترن مناسب تهیه کرد.
پترنی که علامت آکولاد باز وبسته {} و کاراکتری داخل اون رو انتخاب میکنه بصورت زیر می باشه :


\{.*?\}

حالا این پترن چی میگه ؟ میگه در متن برو دنبال مقداری بگرد که اولش با علامت } شروع شده باشه }\ بعد از علامت آکولاد باز هر کاراکتری رو انتخاب کن برای هر کاراکتر از کاراکتر نقطه استفاده می کنیم و برای به هر تعداد(صفر یا بیشتر ) از علامت ستاره * استفاده می کنیم بعد از علامت ستاره علامت ? اضافه می کنیم و با درج این علامت میگیم اگه هیچ کاراکتری هم اگه نبود اشکال نداره ، شرط آخرمون هم اینکه مقدار با علامت آکولاد بسته { خاتمه پیداکنه{\
حالا بجای این مقداری که با قاعده فوق مشخص کردیم میخواهیم مقدار فرضا عدد 9 رو جایگزین کنیم .
خلاصه برای اجرای عمل جایگزینی فوق از کد زیر استفاده می کنیم :

Dim regEx As Object
Dim html_input As String
Set regEx = CreateObject("VBScript.RegExp")
regEx.Pattern = "\{.*?\}"
regEx.Global = True
Text1 = regEx.Replace(Text1, "9")

در نتیجه رشته خروجی yadda yadda 9 omtty doom 9 loppy loop می باشه

atf1379
یک شنبه 13 آبان 1403, 12:37 عصر
درود بر جناب آقای بهرامی گرامی که مثل همیشه مطالب تخصصی را بسیار ساده و قابل فهم توضیح میدهند.
از جناب mazoolagh (https://barnamenevis.org/member.php?9893-mazoolagh) عزیز هم بخاطر ارائه نظرات تخصیصی قدردانی میشود.
درود بر هر دو بزرگوار

eb_1345
یک شنبه 13 آبان 1403, 14:17 عصر
برای اینکه متوجه بشیم مطالب آموزشی تا اینجا برای دوستان علاقمند مفید فایده بوده در این پست سوالی ساده طرح می کنیم

با توجه به توضیحات پست 49 میخواهیم یک پترن بنویسیم که در متن انگلیسی زیر کلماتی که حرف آخرشون e میباشه بجای حرف e آخر کلمه عدد 9 جایگزین بشه . مثل کلمه home به hom9 تبدیل بشه.
تاکید می کنم این جایگزینی فقط باید در کلماتی که حرف آخرشون e میباشه صورت بگیره نه کلماتی که حرف e در اول یا وسطشون می باشه
برای تست از کدهای پست 49 استفاده کنین!
Social media star Peanut the Squirrel has been euthanized after being seize from NY home

قطعاً جواب این سوال برای دوستانی که مطالب ارائه شده رو با دقت مرور کرده ان بسیار ساده خواهد بود .
متن خروجی باید بصورت زیر باشه :
Social media star Peanut th9 Squirrel has been euthanized after being seiz9 from NY hom9

eb_1345
یک شنبه 13 آبان 1403, 14:35 عصر
نسخه آپدیت شده برنامه نمونه

eb_1345
یک شنبه 13 آبان 1403, 20:37 عصر
3- موضوع کاربردی اینجاست:
.Pattern = "(.)\1{9}"
در واقع الگوریتم انتخاب شده برای کد ملی یک اشکال داره و اونهم این هست که
اگر همه ارقام یکی باشن معتبر نشون میده (البته در واقعیت ما باید از یک api استفاده کنیم و کد ملی رو با سایر مشخصات تطبیق بدیم).
در این پترن از چیزی استفاده کردیم که بهش back-reference میگن و برای capture group شماره 1 - 9 بار تکرار میشه (جمعا 10 بار).
وگرنه میبایست همه این 10 حالت رو چک میکردیم - حالا یا یکی یکی یا در یک حلقه.
.

با سلام و وقت بخیر
در تابع فوق نمیشه از طریق کد زیر تکراری بودن 10 رقم کد ملی رو چک کرد؟


If Len(SSID) - Len(Replace(SSID, Left(SSID, 1), "")) = 10 Then
IsValidSSID = False
Exit Function
End If

mazoolagh
دوشنبه 14 آبان 1403, 17:33 عصر
با سلام و وقت بخیر
در تابع فوق نمیشه از طریق کد زیر تکراری بودن 10 رقم کد ملی رو چک کرد؟


If Len(SSID) - Len(Replace(SSID, Left(SSID, 1), "")) = 10 Then
IsValidSSID = False
Exit Function
End If


سلام دوباره
قطعا میشه،
این دست شماست که چه روشی پیاده کنین.
گاهی در حالتهایی خاص (مثل اینجا که تکراری بودن شامل همه استرینگ هست) میشه کدهایی شبیه این نوشت،
یا حتی کوتاهترش کرد:
If Replace(SSID, Left(SSID, 1), "")="" Then
IsValidSSID = False
Exit Function
End If

شما هر کاری رو که با regex انجام میدین،
با همین توابع string و split و ... هم میشه.
گاهی حتی ساده تر هم هست (پست 44)، و خب گاهی ممکنه خیلی سخت تر باشه - ولی میشه.

به طور مطلق نمبتونین بگین کدوم برتری داره: در سناریو هست که با توجه به شرایط مشخص میشه.

در همین مثال اگر مسئله به این صورت تغییر کنه:
آیا یک استرینگ شامل حروف تکراری (2 یا بیشتر) پشت سر هم هست؟
مثل abcdddefgghhhhxyyyxx که باید اینها رو پیدا کنه: abcdddefgghhhhxyyyxx
دیگه کدی که فقط با توابع استرینگ پیاده شده باشه، شاید نوشتنش به سادگی این مثال نباشه (همیشه شدنی هست)
ولی همین روش back-refrence با یک پترن ساده پاسخگو هست:

(.)\1+
دیگه انتخابش با شماست که یک Trade-off بین زمان رسیدن به پاسخ، سادگی نگهداری و دیباگ، خوانایی، پرفورمنس و ... انجام بدین.

mazoolagh
دوشنبه 14 آبان 1403, 17:45 عصر
در پست شماره 34 یک بخش هست که چند بار میخواستم بپرسم و فراموش کردم:

البته در متن انگلیسی برای بدست آوردن تعداد یک کاراکتر یا کلمه خاص در یک متن میتونیم با ترکیب تابع Len و Replace اینکار رو انجام بدهیم
مثلاً در همون مثال اولی میخواهم تعداد حرف o رو در متن www.bahrami125@yahoo.com (http://www.bahrami125@yahoo.com) ورودی بدست بیاریم.
برای اینکار تابع رو بصورت زیر مینویسیم :
CountWords = Len("www.bahrami125@yahoo.com") - Len(Replace("www.bahrami125@yahoo.com", "o", ""))
ولی با این کد تعداد در متون فارسی درست بدست نمیاد ولی تابعی که در اون از پترن استفاده شده برای بدست آوردن تعداد در هر دو حالت فارسی و انگلیسی مناسبتره

این بخش رو میشه بیشتر توضیح بدین که روی چه استرینگ هایی تست کردین که جواب نداده؟

eb_1345
دوشنبه 14 آبان 1403, 19:01 عصر
در پست شماره 34 یک بخش هست که چند بار میخواستم بپرسم و فراموش کردم:


این بخش رو میشه بیشتر توضیح بدین که روی چه استرینگ هایی تست کردین که جواب نداده؟
با عرض سلام
لطفاً مورد زیر رو امتحان بفرمائید!


MsgBox Len("در حالي که من با چشم هاي خودم ديدم و اين در حالي بود که ....") - Len(Replace("در حالي که من با چشم هاي خودم ديدم و اين در حالي بود که ....", "حالي", ""))

البته ناگفته نمونه که تعداد خروجی همیشه هم اشتباه نیست . شاید استفاده از تابع Trim برای آرگومان مقدار مورد جستجو مشکل رو برطرف کنه

atf1379
سه شنبه 15 آبان 1403, 05:55 صبح
برای اینکه متوجه بشیم مطالب آموزشی تا اینجا برای دوستان علاقمند مفید فایده بوده در این پست سوالی ساده طرح می کنیم

با توجه به توضیحات پست 49 میخواهیم یک پترن بنویسیم که در متن انگلیسی زیر کلماتی که حرف آخرشون e میباشه بجای حرف e آخر کلمه عدد 9 جایگزین بشه . مثل کلمه home به hom9 تبدیل بشه.
تاکید می کنم این جایگزینی فقط باید در کلماتی که حرف آخرشون e میباشه صورت بگیره نه کلماتی که حرف e در اول یا وسطشون می باشه
برای تست از کدهای پست 49 استفاده کنین!
Social media star Peanut the Squirrel has been euthanized after being seize from NY home

قطعاً جواب این سوال برای دوستانی که مطالب ارائه شده رو با دقت مرور کرده ان بسیار ساده خواهد بود .
متن خروجی باید بصورت زیر باشه :
Social media star Peanut th9 Squirrel has been euthanized after being seiz9 from NY hom9

باسلام
فکر کنم باید از پترن زیر استفاده کنیم


[e]\b

برای حل این مسئله از مثال مربوط به علامت \b در فایل نمونه اسفاده کردم.





شما هر کاری رو که با regex انجام میدین،
با همین توابع string و split و ... هم میشه.
گاهی حتی ساده تر هم هست (پست 44)، و خب گاهی ممکنه خیلی سخت تر باشه - ولی میشه.

به طور مطلق نمبتونین بگین کدوم برتری داره: در سناریو هست که با توجه به شرایط مشخص میشه.


بنده هر کاری کردم نتوانستم مسئله بالا رو از طریق توابع string و split حل کنم . اگر استاد راهنمائی کنن که از طریق توابع فوق به چه صورتی باید برای حل مسئله فوق استفاده کنیم ممنون میشوم

mazoolagh
سه شنبه 15 آبان 1403, 15:29 عصر
با عرض سلام
لطفاً مورد زیر رو امتحان بفرمائید!


MsgBox Len("در حالي که من با چشم هاي خودم ديدم و اين در حالي بود که ....") - Len(Replace("در حالي که من با چشم هاي خودم ديدم و اين در حالي بود که ....", "حالي", ""))

البته ناگفته نمونه که تعداد خروجی همیشه هم اشتباه نیست . شاید استفاده از تابع Trim برای آرگومان مقدار مورد جستجو مشکل رو برطرف کنه

سلام دوباره
استرینگ ها در اکسس یونیکد نگهداری میشن و توابع استرینگ و regex به زبان حساس نیست.

اونچه برداشت میکنم این هست که شما دنبال تعداد "حالی" هستین، که 4 حرفی هست،
ولی کد پست 34 برای یک استرینگ یک کارآکتری درست کار میکنه - در واقع یک حالت خاص هست.
شما باید اندازه استرینگی رو که دنبالش هستین در محاسبات بیارین:
PUBLIC FUNCTION CountOf( _
Expression AS STRING, _
Find AS STRING _
) AS INTEGER
DIM LenE AS INTEGER, LenF AS INTEGER
LenE=LEN(Expression)
LenF=LEN(Find)
IF LenF=0 OR LenE=0 THEN
CountOf=0
ELSE
CountOf=(LenE - LEN(REPLACE(Expression , Find , "")))/LenF
ENDIF
END FUNCTION

mazoolagh
سه شنبه 15 آبان 1403, 15:36 عصر
با عرض سلام
لطفاً مورد زیر رو امتحان بفرمائید!


MsgBox Len("در حالي که من با چشم هاي خودم ديدم و اين در حالي بود که ....") - Len(Replace("در حالي که من با چشم هاي خودم ديدم و اين در حالي بود که ....", "حالي", ""))

البته ناگفته نمونه که تعداد خروجی همیشه هم اشتباه نیست . شاید استفاده از تابع Trim برای آرگومان مقدار مورد جستجو مشکل رو برطرف کنه
روش های دیگه هم میشه به کار برد:
PUBLIC FUNCTION CountOf( _
Expression AS STRING, _
Find AS STRING _
) AS INTEGER
IF LEN(Find)=0 OR LEN(Expression)=0 THEN
CountOf=0
ELSE
DIM X AS VARIANT
X=SPLIT(Expression , Find)
CountOf=UBOUND(X)
ENDIF
END FUNCTION

PUBLIC FUNCTION CountOf( _
Expression AS STRING, _
Find AS STRING _
) AS INTEGER

IF LEN(Find)=0 OR LEN(Expression)=0 THEN
CountOf=0
EXIT FUNCTION
ENDIF

DIM Start AS INTEGER
Start=1
DO WHILE INSTR(Start , Expression , Find) > 0
CountOf=CountOf + 1
Start=1 + INSTR(Start , Expression , Find)
LOOP
END FUNCTION

mazoolagh
سه شنبه 15 آبان 1403, 15:42 عصر
نوشته شده توسط mazoolagh https://barnamenevis.org/images/buttons/viewpost-left.png (https://barnamenevis.org/showthread.php?p=2479211#post2479211)
شما هر کاری رو که با regex انجام میدین،
با همین توابع string و split و ... هم میشه.
گاهی حتی ساده تر هم هست (پست 44)، و خب گاهی ممکنه خیلی سخت تر باشه - ولی میشه.

به طور مطلق نمبتونین بگین کدوم برتری داره: در سناریو هست که با توجه به شرایط مشخص میشه.
بنده هر کاری کردم نتوانستم مسئله بالا رو از طریق توابع string و split حل کنم . اگر راهنمائی کنن که از طریق توابع فوق به چه صورتی باید برای حل مسئله فوق استفاده کنیم ممنون میشوم

منظورتون مسئله پست 51 هست؟
ساده است که!

تا کجا پیش رفتین؟

eb_1345
سه شنبه 15 آبان 1403, 16:45 عصر
سلام دوباره
استرینگ ها در اکسس یونیکد نگهداری میشن و توابع استرینگ و regex به زبان حساس نیست.

اونچه برداشت میکنم این هست که شما دنبال تعداد "حالی" هستین، که 4 حرفی هست،
ولی کد پست 34 برای یک استرینگ یک کارآکتری درست کار میکنه - در واقع یک حالت خاص هست.
شما باید اندازه استرینگی رو که دنبالش هستین در محاسبات بیارین:
PUBLIC FUNCTION CountOf( _
Expression AS STRING, _
Find AS STRING _
) AS INTEGER
DIM LenE AS INTEGER, LenF AS INTEGER
LenE=LEN(Expression)
LenF=LEN(Find)
IF LenF=0 OR LenE=0 THEN
CountOf=0
ELSE
CountOf=(LenE - LEN(REPLACE(Expression , Find , "")))/LenF
ENDIF
END FUNCTION

احسنت !:تشویق::تشویق::تشویق:

eb_1345
چهارشنبه 16 آبان 1403, 00:30 صبح
همونطور که در پست 34 اشاره شد از تابع زیر هم برای بدست اوردن تعداد مقدار مورد جستجو میشه استفاده کرد :


Public Function RegExCountWords(ByVal Text As String, PatternText As String) As Long
Dim regx
Set regx = CreateObject("VBScript.RegExp")
regx.Pattern = PatternText
regx.Global = True
RegExCountWords = regx.Execute(Text).Count
End Function

mazoolagh
شنبه 26 آبان 1403, 17:34 عصر
برای اینکه متوجه بشیم مطالب آموزشی تا اینجا برای دوستان علاقمند مفید فایده بوده در این پست سوالی ساده طرح می کنیم

با توجه به توضیحات پست 49 میخواهیم یک پترن بنویسیم که در متن انگلیسی زیر کلماتی که حرف آخرشون e میباشه بجای حرف e آخر کلمه عدد 9 جایگزین بشه . مثل کلمه home به hom9 تبدیل بشه.
تاکید می کنم این جایگزینی فقط باید در کلماتی که حرف آخرشون e میباشه صورت بگیره نه کلماتی که حرف e در اول یا وسطشون می باشه
برای تست از کدهای پست 49 استفاده کنین!
Social media star Peanut the Squirrel has been euthanized after being seize from NY home

قطعاً جواب این سوال برای دوستانی که مطالب ارائه شده رو با دقت مرور کرده ان بسیار ساده خواهد بود .
متن خروجی باید بصورت زیر باشه :
Social media star Peanut th9 Squirrel has been euthanized after being seiz9 from NY hom9

Option Compare Database
Option Explicit

Const Expression = "Eager elephants delighted in the serene evening, " + vbCrLf + _
"feeling the gentle breeze under the green trees where they could see the sea." + vbCrLf + _
"e E e se(25) agree- free8 THE e 87e e see. glee_ drive."

Function Replace_RegEx() As String
With New RegExp
.pattern = "e\b"
.Global = True
.IgnoreCase = False
.Multiline = True
Replace_RegEx = .Replace(Expression, "9")
End With
End Function

تست:
? replace_regex

Eager elephants delighted in th9 seren9 evening,
feeling th9 gentl9 breez9 under th9 green trees wher9 they could se9 th9 sea.
9 E 9 s9(25) agre9- free8 THE 9 879 9 se9. glee_ driv9.

mazoolagh
شنبه 26 آبان 1403, 17:41 عصر
برای اینکه متوجه بشیم مطالب آموزشی تا اینجا برای دوستان علاقمند مفید فایده بوده در این پست سوالی ساده طرح می کنیم

با توجه به توضیحات پست 49 میخواهیم یک پترن بنویسیم که در متن انگلیسی زیر کلماتی که حرف آخرشون e میباشه بجای حرف e آخر کلمه عدد 9 جایگزین بشه . مثل کلمه home به hom9 تبدیل بشه.
تاکید می کنم این جایگزینی فقط باید در کلماتی که حرف آخرشون e میباشه صورت بگیره نه کلماتی که حرف e در اول یا وسطشون می باشه
برای تست از کدهای پست 49 استفاده کنین!
Social media star Peanut the Squirrel has been euthanized after being seize from NY home

قطعاً جواب این سوال برای دوستانی که مطالب ارائه شده رو با دقت مرور کرده ان بسیار ساده خواهد بود .
متن خروجی باید بصورت زیر باشه :
Social media star Peanut th9 Squirrel has been euthanized after being seiz9 from NY hom9

بدون استفاده از regex

Function Replace_Simple() As String
Dim x As String
x = Expression
Dim start As Integer
start = InStr(1, x, "e", vbBinaryCompare)
Do While start > 0
If Not Mid(x, start + 1, 1) Like "[A-Za-z0-9_]" Then
Mid(x, start, 1) = "9"
End If
start = InStr(1 + start, x, "e", vbBinaryCompare)
Loop
Replace_Simple = x
End Function

تست:
? replace_simple

Eager elephants delighted in th9 seren9 evening,
feeling th9 gentl9 breez9 under th9 green trees wher9 they could se9 th9 sea.
9 E 9 s9(25) agre9- free8 THE 9 879 9 se9. glee_ driv9.

اطمینان از این که هر دو روش پاسخ یکسان دارن:
Debug.Print StrComp(Replace_RegEx, Replace_Simple, vbBinaryCompare) = 0
که true هست.

eb_1345
شنبه 26 آبان 1403, 19:20 عصر
بدون استفاده از regex

Function Replace_Simple() As String
Dim x As String
x = Expression
Dim start As Integer
start = InStr(1, x, "e", vbBinaryCompare)
Do While start > 0
If Not Mid(x, start + 1, 1) Like "[A-Za-z0-9_]" Then
Mid(x, start, 1) = "9"
End If
start = InStr(1 + start, x, "e", vbBinaryCompare)
Loop
Replace_Simple = x
End Function

تست:
? replace_simple

Eager elephants delighted in th9 seren9 evening,
feeling th9 gentl9 breez9 under th9 green trees wher9 they could se9 th9 sea.
9 E 9 s9(25) agre9- free8 THE 9 879 9 se9. glee_ driv9.

اطمینان از این که هر دو روش پاسخ یکسان دارن:
Debug.Print StrComp(Replace_RegEx, Replace_Simple, vbBinaryCompare) = 0
که true هست.
عالی مثل همیشه!:تشویق::تشویق::تشویق:

atf1379
یک شنبه 27 آبان 1403, 05:51 صبح
بنده هر کاری کردم نتوانستم مسئله بالا رو از طریق توابع string و split حل کنم . اگر استاد راهنمائی کنن که از طریق توابع فوق به چه صورتی باید برای حل مسئله فوق استفاده کنیم ممنون میشوم


بدون استفاده از regex

Function Replace_Simple() As String
Dim x As String
x = Expression
Dim start As Integer
start = InStr(1, x, "e", vbBinaryCompare)
Do While start > 0
If Not Mid(x, start + 1, 1) Like "[A-Za-z0-9_]" Then
Mid(x, start, 1) = "9"
End If
start = InStr(1 + start, x, "e", vbBinaryCompare)
Loop
Replace_Simple = x
End Function

تست:
? replace_simple

Eager elephants delighted in th9 seren9 evening,
feeling th9 gentl9 breez9 under th9 green trees wher9 they could se9 th9 sea.
9 E 9 s9(25) agre9- free8 THE 9 879 9 se9. glee_ driv9.

اطمینان از این که هر دو روش پاسخ یکسان دارن:
Debug.Print StrComp(Replace_RegEx, Replace_Simple, vbBinaryCompare) = 0
که true هست.

ممنون استاد
لطف کردین

mazoolagh
یک شنبه 27 آبان 1403, 17:51 عصر
ممنون
لطف کردین

پترن پست شماره 63 در واقع همون پترن پیشنهادی شما در پست شماره 57 هست
که [] حذف شده - چون فقط یک کارآکتر بررسی میشه بهش نیازی نیست.

mazoolagh
یک شنبه 27 آبان 1403, 18:07 عصر
با اجازه جناب بهرامی،
اگر بخواهیم ببینیم چه واژه هایی در یک عبارت تکرار شدن چه پترنی باید استفاده کنیم؟

eb_1345
دوشنبه 28 آبان 1403, 01:17 صبح
با اجازه جناب بهرامی،
اگر بخواهیم ببینیم چه واژه هایی در یک عبارت تکرار شدن چه پترنی باید استفاده کنیم؟
عرض سلام و ارادت خدمت استاد مازولاق عزیز !
اختیار دارین بزرگوار !
قطعا حضور جنابعالی همراه با طرح سوالات و ارائه نکات فنی و تخصص هم باعث پربار شدن این تاپیک آموزشی میشه وهم دلگرمی بنده برای ادامه فعالیت

eb_1345
دوشنبه 28 آبان 1403, 01:34 صبح
در فرم تشریح و تفسیر عبارات با قاعده فایل آپدیت شده ضمیمه رکوردی با عنوان انواع پترن های کاربردی ایجاد کرده ام که در قسمت سابفرم این رکورد تعداد 112 عبارات Regex که ممکنه در شرایط مختلف به اونها نیاز داشته باشیم اضافه نموده ام . برای دسترسی به پترن های فوق کلمه All رو از کمبوباکس جستجو انتخاب نمائین!

mazoolagh
دوشنبه 28 آبان 1403, 12:49 عصر
در فرم تشریح و تفسیر عبارات با قاعده فایل آپدیت شده ضمیمه رکوردی با عنوان انواع پترن های کاربردی ایجاد کرده ام که در قسمت سابفرم این رکورد تعداد 112 عبارات Regex که ممکنه در شرایط مختلف به اونها نیاز داشته باشیم اضافه نموده ام . برای دسترسی به پترن های فوق کلمه All رو از کمبوباکس جستجو انتخاب نمائین!

بسیار هم عالی!
شایسته است که این تاپیک sticky بشه!

===========

در تصویر اینجور بنظر میاد که رکورد تکراری هم هست، شایدم اشتباه میکنم.

mazoolagh
دوشنبه 28 آبان 1403, 12:59 عصر
در فرم تشریح و تفسیر عبارات با قاعده فایل آپدیت شده ضمیمه رکوردی با عنوان انواع پترن های کاربردی ایجاد کرده ام که در قسمت سابفرم این رکورد تعداد 112 عبارات Regex که ممکنه در شرایط مختلف به اونها نیاز داشته باشیم اضافه نموده ام . برای دسترسی به پترن های فوق کلمه All رو از کمبوباکس جستجو انتخاب نمائین!

در مورد تشخیص حروف فارسی:
فکر کنم بهتره یک تاپیک جدا واسش بزنین،
و این پترن که در تصویر هست احتمالا نیاز به تکمیل داشته باشه.

eb_1345
دوشنبه 28 آبان 1403, 21:02 عصر
اگر بخواهیم ببینیم چه واژه هایی در یک عبارت تکرار شدن چه پترنی باید استفاده کنیم؟

ظاهراً دوستان علاقه چندانی به موضوع این تاپیک ندارند .
با توجه به اینکه یجورائی پترن مورد نظر جنابعالی رو به فایل آپدیت شده در پست 70 اضافه نموده ام ولی ...
بعد از این همه مدت که از زمان ایجاد این تاپیک میگذره از دوستان انتظار میرفت که در خصوص مطالب آموزشی اظهار نظری هرچند مختصر ارائه نماین ولی متاسفانه به استثناء یک نفر بقیه دوستان صرفاً نظاره گر مطالب هستن

mazoolagh
سه شنبه 29 آبان 1403, 17:08 عصر
ظاهراً دوستان علاقه چندانی به موضوع این تاپیک ندارند ... صرفاً نظاره گر مطالب هستن
این regex بسیار بدقلق هست و شیب منحنی یادگیری اش هم کمه!
گذشته از این: بیشتر و نزدیک به همه، فقط برای رفع مشکلی که همون لحظه دچارش هستن اینجا میان و نه برای یاد گرفتن.
ولی در طی زمان بازدیدکننده ها با جستجو اینجا رو پیدا و ازش استفاده میکنن، پس دلسرد نباشین و ادامه بدین - تو نیکی میکن و در دجله انداز!


... پترن مورد نظر (پست شماره 68) رو به فایل آپدیت شده در پست 70 اضافه نموده ام ...
آره، در تصویر هم دیده میشه - ولی اون پترن (در همه حالتها) درست نیست !
باید سر فرصت با مثال نشون بدم.

eb_1345
سه شنبه 29 آبان 1403, 22:22 عصر
آره، در تصویر هم دیده میشه - ولی اون پترن (در همه حالتها) درست نیست !

من چند مورد رو امتحان کردم واژه های تکراری رو نمایش میده ولی بعضی از اونها سلکت نمیشن
البته فکر کنم بشه که با همین پترن اول موارد تکراری ( همون هائی که سلکت میشن) پشت سر هم بصورت یک رشته تبدیل کرد و بعد موارد تکراری این رشته رو حذف کرد . که در نهایت در رشته واژه هائی که تکرار شده اند بدست بیاد.

eb_1345
چهارشنبه 30 آبان 1403, 15:26 عصر
البته فکر کنم بشه که با همین پترن اول موارد تکراری ( همون هائی که سلکت میشن) پشت سر هم بصورت یک رشته تبدیل کرد و بعد موارد تکراری این رشته رو حذف کرد . که در نهایت در رشته واژه هائی که تکرار شده اند بدست بیاد.
لطفاً توابع پائین رو تست بفرمائین !


Public Function RemoveDupesInString(strText As String, Optional strDelim As String = " ") As String
Dim varArray As Variant
Dim intI As Integer
Dim strOut As String
strText = " " & strText & " "
varArray = Split(strText, strDelim)
For intI = 0 To UBound(varArray) - 1
If InStr(strOut, varArray(intI)) = 0 Then
strOut = strOut & varArray(intI) & " "
End If
Next
RemoveDupesInString = Trim(strOut)
End Function




Public Function DupesRegexp(StrReg As String)
Dim regx
Dim Matches As Object
Dim Match As Object
Dim Str As String
Set regx = CreateObject("VBScript.RegExp")
regx.Global = True
regx.IgnoreCase = True
regx.Multiline = True
regx.Pattern = "(\b\w+\b)(?=.*\b\1\b)"
Set Matches = regx.Execute(StrReg)
For Each Match In Matches
Str = Str & " " & Match.Value
Next
Str = Right(Str, Len(Str) - 1)
MsgBox RemoveDupesInString(Str)
End Function


Call DupesRegexp("Secure, smart, and easy to use email Get more done with Gmail. Now integrated with google Chat, Google Meet, and more, all in one place.")

eb_1345
چهارشنبه 30 آبان 1403, 16:17 عصر
ایراداتی در کد های فوق وجود داره که نیاز به چکش کاری استاد داره

mazoolagh
چهارشنبه 30 آبان 1403, 18:50 عصر
سلام دوباره
الان یه کمی مطلب پیچیده شد - بذارین یکی یکی بریم جلو.

1- این پترن

(\b\w+\b)(?=.*\b\1\b)
دو تکه اس،
بهتره پیش از هر کاری عملکرد تکه اول رو که قراره کلمه ها رو پیدا کنه بررسی کنیم:
Sub duplicate_words()
Const pattern = "(\b\w+\b)"
Dim Parts()
Parts = Array( _
"This is the first part,", _
"and this is the second part,", _
"now and then", _
"now another part added.")

test_it Join(Parts, " "), pattern
test_it Join(Parts, vbCrLf), pattern
End Sub

Sub test_it(Expression As String, pattern As String)
Dim mc As MatchCollection
Dim m As match

With New RegExp
.Global = True
.IgnoreCase = True
.Multiline = True
.pattern = pattern
Set mc = .Execute(Expression)
End With
Debug.Print "pattern=" & pattern, "count=" & mc.Count
For Each m In mc
Debug.Print m & " ";
Next
Debug.Print
End Sub

خروجی:

pattern=(\b\w+\b) count=18
This is the first part and this is the second part now and then now another part added
pattern=(\b\w+\b) count=18
This is the first part and this is the second part now and then now another part added

همینجور که دیده میشه این پترن با نقطه و ویرگول (چیزهای دیگه هست که میتونین خودتون تست کنین)
رفتار مشخصی داره و اون ها رو کنار میذاره -
که اگر در سناریو کاری ما مشکلی نداشته باشه خب همین خوبه و تا اینجا کلمه ها رو درست تشخیص میده (بخش پیدا کردن تکراری رو جدا بررسی میکنیم)،
وگرنه باید یک فکر دیگه برداریم.

mazoolagh
چهارشنبه 30 آبان 1403, 19:12 عصر
حالا وضعیت زیر رو بببینیم:
test_it "sin(5x) a+b=c 192.168.1.1 first-name last_name [date] (\b\w+\b) end. ", pattern
نتیجه:

pattern=(\b\w+\b) count=17
sin 5x a b c 192 168 1 1 first name last_name date b w b end

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

mazoolagh
چهارشنبه 30 آبان 1403, 19:22 عصر
در یک سناریوی دقیقتر، کلمه اینجوری تعریف میشه که هر رشته ای از کارآکترها که white space نداشته باشه:
test_it "sin(5x) a+b=c 192.168.1.1 first-name last_name [date] (\b\w+\b) end. ", "(\S+)"

pattern=(\S+) count=8
sin(5x) a+b=c 192.168.1.1 first-name last_name [date] (\b\w+\b) end.

انتخاب پترن به سناریوی ما برمیگرده - اینجور نیست که بگیم یکی بهتر هست،
شاید هیچکدام اینها پاسخگوی نیاز ما نباشه و لازم باشه یک پترن مناسب طراحی کنیم.

mazoolagh
چهارشنبه 30 آبان 1403, 19:41 عصر
2- حالا میریم سر وقت تکه دوم که قرار هست تکراری ها رو با back reference به تکه اول پیدا کنه:
test_it Join(Parts, " "), pattern
test_it Join(Parts, vbCrLf), pattern


pattern=(\b\w+\b)(?=.*\b\1\b) count=7
This is the part and part now
pattern=(\b\w+\b)(?=.*\b\1\b) count=0
اینجا کاملا واضح هست که این پترن در حالت single line مشکل نداره (احتمالا و با توجه به شرایط)،
ولی وقتی در متن vbcrlf داریم کار نمیکنه!

mazoolagh
پنج شنبه 01 آذر 1403, 17:58 عصر
از اینجا به بعد برای تشخیص تکراری ها میتونیم 2 راه در پیش بگیریم:
- یکی این که وقت و انرژی بگذاریم و یک پترن دقیق و در برگیرنده همه حالت ها بسازیم (راه سخت و زمانبر)
- با یکی از پترن های پست 70 یا 80 (یا هر پترن دیگه که شرایط سناریو ما رو برآورده میکن)
یک لیست از همه کلمه ها بسازیم و از روی این لیست تکراری ها رو پیدا کنیم (راه ساده و سریع)

در کد نمونه زیر پس از درست کردن یک لیست از همه کلمه ها،
یک دیکشنری میسازیم و در یک لوپ روی لیست،
اگر یک کلمه در دیکشنری نبود اون رو اضافه میکنیم (بعنوان کلید) و تعداش رو 1 میگذاریم (بعنوان item)،
و اگر از قبل وجود داشت مقدار item رو یکی اضافه میکنیم.

Dim Parts()
Parts = Array( _
"USE IRAN;", _
"WITH ALLCITIES AS (", _
"SELECT City , CityID, DistrictID FROM Cities WHERE CITY IS NOT NULL", _
"UNION", _
"SELECT RuralDistrict AS City, RuralDistrictID AS CityID, DistrictID FROM RuralDistricts WHERE RuralDistrict IS NOT NULL", _
")", _
"SELECT", _
" states.State , states.StateID,", _
" Counties.County , Counties.CountyID,", _
" Districts.District , Districts.DistrictID,", _
" ALLCITIES.City , ALLCITIES.CityID", _
"FROM states", _
" LEFT JOIN Counties ON states.StateID=Counties.StateID", _
" LEFT JOIN Districts ON Counties.CountyID=Districts.CountyID", _
" LEFT JOIN ALLCITIES ON Districts.DistrictID=ALLCITIES.DistrictID", _
"ORDER BY states.State, Counties.County, Districts.District, ALLCITIES.City", _
"COLLATE Persian_100_CI_AI", _
"FOR JSON AUTO ,WITHOUT_ARRAY_WRAPPER;")

Const pattern = "(\S+)"
Dim mc As MatchCollection
Dim m As match

With New RegExp
.Global = True
.IgnoreCase = True
.Multiline = True
.pattern = pattern
Set mc = .Execute(Join(Parts, vbCrLf))
End With

Dim d As New Dictionary
For Each m In mc
If d.Exists(m.Value) Then
d(m.Value) = d(m.Value) + 1
Else
d.Add key:=m.Value, item:=1
End If
Next
Dim item
Dim i As Integer
For i = 0 To d.Count - 1
If d.Items(i) > 1 Then
Debug.Print d.Keys(i), d.Items(i)
End If
Next
Set d = Nothing

ALLCITIES 2
AS 3
SELECT 3
, 5
CityID, 2
DistrictID 2
FROM 3
WHERE 2
IS 2
NOT 2
NULL 2
RuralDistrict 2
ALLCITIES.City 2
LEFT 3
JOIN 3
ON 3

mazoolagh
پنج شنبه 01 آذر 1403, 18:04 عصر
لطفاً توابع پائین رو تست بفرمائین !


Public Function RemoveDupesInString(strText As String, Optional strDelim As String = " ") As String
Dim varArray As Variant
Dim intI As Integer
Dim strOut As String
strText = " " & strText & " "
varArray = Split(strText, strDelim)
For intI = 0 To UBound(varArray) - 1
If InStr(strOut, varArray(intI)) = 0 Then
strOut = strOut & varArray(intI) & " "
End If
Next
RemoveDupesInString = Trim(strOut)
End Function




Public Function DupesRegexp(StrReg As String)
Dim regx
Dim Matches As Object
Dim Match As Object
Dim Str As String
Set regx = CreateObject("VBScript.RegExp")
regx.Global = True
regx.IgnoreCase = True
regx.Multiline = True
regx.Pattern = "(\b\w+\b)(?=.*\b\1\b)"
Set Matches = regx.Execute(StrReg)
For Each Match In Matches
Str = Str & " " & Match.Value
Next
Str = Right(Str, Len(Str) - 1)
MsgBox RemoveDupesInString(Str)
End Function


Call DupesRegexp("Secure, smart, and easy to use email Get more done with Gmail. Now integrated with google Chat, Google Meet, and more, all in one place.")





سلام دوباره
گویا این کد برای پاک کردن موارد تکراری هست - که از پرسش اصلی پست 68 سختتره (فقط پیدا کردن تکراری ها)!
در نگاه اول کد زیر حالت خاص هست:
strText = " " & strText & " "

eb_1345
پنج شنبه 01 آذر 1403, 21:20 عصر
اگر بخواهیم ببینیم چه واژه هایی در یک عبارت تکرار شدن چه پترنی باید استفاده کنیم؟


من چند مورد رو امتحان کردم واژه های تکراری رو نمایش میده ولی بعضی از اونها سلکت نمیشن
البته فکر کنم بشه که با همین پترن اول موارد تکراری ( همون هائی که سلکت میشن) پشت سر هم بصورت یک رشته تبدیل کرد و بعد موارد تکراری این رشته رو حذف کرد . که در نهایت در رشته واژه هائی که تکرار شده اند بدست بیاد.


سلام دوباره
گویا این کد برای پاک کردن موارد تکراری هست - که از پرسش اصلی پست 68 سختتره (فقط پیدا کردن تکراری ها)!
در نگاه اول کد زیر حالت خاص هست:
strText = " " & strText & " "
با عرض سلام متقابل
تابع اول برای حذف موارد تکراری لیست واژ های که بوسیله پترن استخراج شده میباشه
فرض می کنیم در متن زیر میخواهیم لیست واژه ها یا کلماتی که تکرار شده اند بدست بیاریم:


Secure, smart, and easy to use email Get more done with Gmail. Now integrated Google with google Chat Google Meet and more all in one Google all and all all All meet all

با استفاده از پترن (\b\w+\b)(?=.*\b\1\b) لیست کلمات تکراری بصورت زیر استخراج میشه:


and more with Google google Google Meet and all all all all All

در لیست فوق می بینیم که مثلاً واژه all (با در نظر گرفتن حساسیت به بزرگی و کوچکی حروف) در متن 6 مرتبه تکرار شده ولی پترن تعداد 5 تای اون رو انتخاب میکنه و دلیلش فعلاً برای بنده معلوم نیست
ولی صرفنظر از اینکه از تعداد واژهای تکرار شده چه واژه هائی انتخاب میشن یا نمیشن پترن فوق در تشخیص واژه های تکراری درست عمل میکنه
حالا اگه ما فقط بخواهیم بدونیم که چه واژه هائی در متن بیشتر از یکبار تکرار شده اند بدون اینکه همه واژه های تکراری در لیست درج بشه با استفاده از تابعی که برای حذف موارد تکراری ایجاد کرده ام موارد تکراری لیست واژه های تکراری اسخراج شده بوسیله پترن رو حذف می کنیم .

که در نهایت با استفاده از تابع حذف واژه های تکراری خروجی لیست استخراج شده بوسیله پترن بصورت زیر نمایش داده میشه:


and more with Google Meet all

یعنی در متن 6 تا کلمه فوق (بیشتر از یکبار) تکرار شده اند.
لطفا فایل نمونه رو بررسی بفرمائین !

mazoolagh
شنبه 03 آذر 1403, 12:54 عصر
سلام دوباره


...
در لیست فوق می بینیم که مثلاً واژه all (با در نظر گرفتن حساسیت به بزرگی و کوچکی حروف) در متن 6 مرتبه تکرار شده ولی پترن تعداد 5 تای اون رو انتخاب میکنه و دلیلش فعلاً برای بنده معلوم نیست
...
در استفاده از back-reference ، خود refrence هم در شمارش میاد.
اگر خاطرتون باشه در پست شماره 45 اشاره کرده بودم (ارقام تکراری در کد ملی).


لطفا فایل نمونه رو بررسی بفرمائین !
پس شما یک بار از روی match collection یک string میسازین،
بعد در اون استرینگ تکراری ها رو حذف میکنین. من اول فکر کردم تکراری ها رو از عبارت اصلی حذف میکنین.
اگر به هر دلیلی نمیخواین از دیکشنری استفاده کنین که خیلی ساده تر هست (پست شماره 82)،
باز هم تابع RemoveDupesInString بنظر اضافه میاد!
شما در همون لوپ For Each Match In Matches در تابع DupesRegexp هم میتونین این کار رو بکنین.

eb_1345
یک شنبه 04 آذر 1403, 19:46 عصر
جستجو در سمت جلو و عقب با استفاده از عبارتهای (=?) و (=>?)
با استفاده از عبارت های فوق تطبیق وقتی صورت می گیره که یا در ادامه یا قبل از اون کاراکتر مورد نظر آمده باشه .
فرض می کنیم در یک متنی دنبال حرف انگلیسی g می گردیم که قبل از اون حرف e باشه
عبارت پترن رو باید بصورت زیر وارد کنیم که تطبیق صورت بگیره :


(?<=e)g

در متن زیر در کلمه integrated حرف g قرار داره که قبلش حرف e میباشه


Get more done with Gmail. Now integrated with Google Chat,

حالا اگه دنبال حرف d بگردیم که بعد از اون حرف o باشه عبارت پترن رو باید بصورت زیر وارد کنیم :


d(?=o)

با پترن فوق در متن بالا حرف d که بعد از اون حرف o باشه در کلمه done وجود داره
البته بجای یک حرف میشه دنبال چند حرف هم گشت . مثلا در متن بالا می خواهیم حروف tegrated جستجو بشه که قبل از این حروف حرف n درج شده باشه . که در این حالت عبارت پترن بصورت زیر وارد میشه :



(?<=n)tegrated

eb_1345
دوشنبه 05 آذر 1403, 12:19 عصر
از مدیر محترم تالار جناب آقای جناب ميرزازاده عزيز بخاطر اصلاح عنوان تاپیک تشکر و سپاسگزاری می نمایم .