مقایسه نام صاحب حساب با نام کاربر پیش از واریز: نرمالسازی نام فارسی
مقایسه نام فارسی با برابری رشتهای واریزهای درست را رد میکند؛ ی و ک، نیمفاصله، ارقام و پیشوندها را نرمال کنید و مقایسه را توکنی انجام دهید.
مقایسهٔ نام صاحب حساب با نام مورد انتظار، اگر با === انجام شود، بخش بزرگی از واریزهای درست را رد میکند. نام فارسی در سیستمهای مختلف شکلهای بسیار متفاوتی دارد، و قبل از هر مقایسه باید به یک شکل واحد برسد.
وقتی استعلام شبا نام صاحب حساب را برمیگرداند، آن نام دادهای خام از شبکهٔ بانکی است. نامی که کاربر در پروفایل خودتان دارد با نام فروشنده در مارکتپلیس یا نام شرکت در قرارداد ممکن است بسیار فرق کند. این مقاله دربارهٔ تبدیل هر دو به شکل قابلمقایسه است، و اینکه کجا باید دست از مقایسهٔ خودکار کشید.
چرا برابری رشتهای شکست میخورد
اینها ناهمخوانیهاییاند که در مقایسهٔ دو نام فارسی پیش میآید:
- ی و ک عربی در برابر فارسی. حرف ی با کد U+064A و حرف ی با کد U+06CC برای چشم یکیاند و برای رایانه نه؛ همین برای «ک» (U+0643 در برابر U+06A9) هم صادق است.
- نیمفاصله. «محمدرضا» و «محمد رضا» و «محمدرضا» همه ممکناند.
- فاصلهٔ دوبل یا ابتدا و انتهای متن.
- ارقام فارسی و لاتین در نام شرکت (مثل «گروه ۱۲»).
- کشیده (ـ) و علامتهای اعراب.
- ترتیب نام و نام خانوادگی که ممکن است معکوس ثبت شود.
- پیشوندها و پسوندها: «آقای»، «خانم»، «دکتر»، «شرکت»، «موسسه»، «سهامی خاص».
در نمونهٔ پاسخ مستندات، نام یک موسسه با نیمفاصلههای پراکنده در میان حروف برگشته. یعنی حتی نام ثبتشده در شبکه هم همیشه تمیز نیست.
یک تابع نرمالسازی
هدف: دو نام معادل، بعد از نرمالسازی دقیقاً برابر باشند.
js const DIGITS = '۰۱۲۳۴۵۶۷۸۹'; export function normName(s) { return s .replace(/[\u064A\u0649]/g, '\u06CC') // ی عربی به فارسی .replace(/\u0643/g, '\u06A9') // ک عربی به فارسی .replace(/[۰-۹]/g, d => DIGITS.indexOf(d)) .replace(/[ً-ٟـ]/g, '') // اعراب و کشیده .replace(/[]/g, ' ') // نیمفاصله و نشانههای جهت .replace(/\s+/g, ' ') .trim(); }
نیمفاصله را عمداً به فاصله تبدیل کردیم، نه حذف. اگر حذفش کنید، «بنیامین» و «بنیامین» برابر میشوند که خوب است، ولی «ماهپری» و «ماهپری» هم برابر میشوند و در برخی موارد دو نام متفاوت را یکی میکنید. مقایسه بر مبنای توکنها (کلمات) بعد از تبدیل به فاصله، رفتار قابلپیشبینیتری دارد.
مقایسهٔ توکنی، نه رشتهای
بعد از نرمالسازی، دو نام را به کلمه بشکنید و مجموعهٔ کلمات را مقایسه کنید:
js const tokens = n => new Set(normName(n).split(' ').filter(Boolean)); function overlap(a, b) { const A = tokens(a), B = tokens(b); const hit = [...A].filter(t => B.has(t)).length; return hit / Math.max(A.size, B.size); }
این کار ترتیب را بیاثر میکند و اگر یک طرف فقط یک کلمهٔ اضافه (مثلاً «دکتر») داشته باشد، امتیاز کمی افت میکند نه صفر. آستانه را با دادههای خودتان تنظیم کنید؛ من آستانهٔ ثابتی پیشنهاد نمیکنم چون بسته به هزینهٔ خطا فرق میکند.
سه خروجی، نه دو
بهجای «تطابق / عدم تطابق»، سه حالت داشته باشید:
| امتیاز | رفتار |
|---|---|
| بسیار بالا (همهٔ کلمات برابر) | عبور خودکار |
| متوسط | نام را به کاربر نشان بده و تأیید بخواه |
| پایین | واریز را متوقف کن، به بررسی انسانی |
حالت وسط مهمترین است. کاربر خودش میداند «شرکت فلان با مسئولیت محدود» همان «فلان» است. ماشین نمیداند.
وقتی فقط نمایش کافی نیست
اگر مقایسهٔ نام برای شما حیاتی است (مثلاً تسویه با فروشندهها)، سرویس تطبیق شبا و نام را هم ببینید. این سرویس مقایسه را در مبدأ انجام میدهد و پاسخ بله یا خیر میدهد. فرق دو راه در راهنمای تطبیق شبا با نام آمده است. من به نظرم برای فرمهایی که کاربر خودش نام را میبیند، استعلام و نمایش، و برای فرایندهایی که انسانی در حلقه نیست، تطبیق مناسبتر است. با این حال ملاک باید هزینه و ریسک خودتان باشد.
آزمون روی داده خودتان
تابع نرمالسازی را با نمونهٔ واقعی نامهای خودتان آزمایش کنید، نه با مثالهای ساختگی. سه گروه نمونه بسازید: جفتهایی که باید یکی شمرده شوند، جفتهایی که نباید، و نام شرکتها. برای هر کدام، خروجی را بنویسید و ببینید چند مورد اشتباه است:
js const must = [['\u0639\u0644\u064A \u0631\u0636\u0627\u06CC\u06CC', 'علی رضایی'], ['محمدرضا احمدی', 'محمد رضا احمدی']]; const mustNot = [['علی رضایی', 'رضا علیزاده']]; for (const [a, b] of must) console.assert(overlap(a, b) === 1); for (const [a, b] of mustNot) console.assert(overlap(a, b) < 0.5);
این آزمونها را در مخزن نگه دارید. هر بار که مورد اشتباهی پیدا کردید، آن را به فهرست اضافه کنید؛ بهمرور تابع شما از هر تابع عمومی بهتر با دادههای شما میخواند.
نام شرکتها جداگانه
شرکتها دردسر ویژهای دارند: «شرکت سهامی خاص فلان»، «فلان (سهامی خاص)»، «فلان». پیشوندها و پسوندهای حقوقی را قبل از مقایسه حذف کنید، ولی فهرست را کوتاه و آگاهانه نگه دارید. حذف بیضابطهٔ کلمات، دو شرکت متفاوت را یکی میکند. برای شرکتها اگر شناسه ملی دارید، مقایسهٔ شناسه از مقایسهٔ نام دقیقتر است.
جایی که دست از خودکارسازی بکشید
برخی ناهمخوانیها با هیچ الگوریتمی حل نمیشود: نام مستعار حساب شرکت، حساب خانوادگی که به نام پدر باز شده و فرزند استفاده میکند، یا نام لاتینشده. برای این موارد، مسیر بررسی انسانی با تصویر مدرک بسازید. هزینهٔ بررسی انسانی برای درصد کوچکی از موارد، کمتر از هزینهٔ واریز اشتباه است. هیچ آستانهای را بدون اندازهگیری روی داده خودتان قطعی ندانید.
لاگ و حریم خصوصی
نامهای مقایسهشده را در لاگ کامل ننویسید. امتیاز و نتیجه کافی است، همراه شناسهٔ پیگیری (requestId). اگر لازم است نام را نگه دارید، آن را در جدولی با دسترسی محدود بگذارید.
آنچه این روش نمیکند
نرمالسازی هویت را تأیید نمیکند. دو نام میتوانند عین هم باشند و دو آدم متفاوت را نشان دهند (نامهای رایج). همچنین حسابی که به نام شخص دیگری باز شده ولی با رضایت او استفاده میشود، همیشه ناهمخوان به نظر میرسد. اگر هویت مهم است، کد ملی هم باید وارد معادله شود، که در تطبیق شبا و کد ملی انجام میشود، نه با مقایسهٔ نام.
نکتهٔ آخر
پیش از هر مقایسه، ی و ک را یکسان کنید، ارقام و علائم را نرمال کنید، نیمفاصله را به فاصله ببرید و به توکن مقایسه کنید. سه خروجی بسازید و حالت میانی را به کاربر بسپارید. نمونههای بالا را با فهرست نامهای واقعی خودتان بیازمایید، چون هیچ تابعی بدون آزمون، با دادههای شما درست کار نمیکند.
خروجی نام را از سرویس استعلام شبا میگیرید و این مقاله فقط نیمهٔ مقایسه را پوشش میدهد.
وبسرویس مرتبط با این راهنما
API استعلام شماره شبا
اعتبار و مشخصات یک شماره شبا را بررسی کنید.
راهنماهای مرتبط
پرسش و تجربه کاربران
اگر این راهنما جوابتان را نداد، همینجا بپرسید. پرسشها را تیمملی ایپیآی پاسخ میدهد و هر نظر پیش از انتشار بررسی میشود.
هنوز پرسشی ثبت نشده، اولین نفر باشید.
