Perl regex يختنق على مثيلات متعددة من مجموعات الأحرف

StackOverflow https://stackoverflow.com/questions/4250120

سؤال

لقد بدأت مع بعض الإخفاقات المجنونة باستخدام preg_replace في PHP وأغليها وصولاً إلى حالة المشكلة المتمثلة في وجود أكثر من فئة حرف واحد باستخدام "i" منقط "و" ı "غير متخلف معًا. فيما يلي حالة اختبار بسيطة في PHP:

<?php
    echo 'match single normal i: ';
    $str = 'mi';
    echo (preg_match('!m[ıi]!', $str)) ? "ok\n" : "fail\n";

    echo 'match single undotted ı: ';
    $str = 'mı';
    echo (preg_match('!m[ıi]!', $str)) ? "ok\n" : "fail\n";

    echo 'match double normal i: ';
    $str = 'misir';
    echo (preg_match('!m[ıi]s[ıi]r!', $str)) ? "ok\n" : "fail\n";

    echo 'match double undotted ı: ';
    $str = 'mısır';
    echo (preg_match('!m[ıi]s[ıi]r!', $str)) ? "ok\n" : "fail\n";
?>

ونفس حالة الاختبار مرة أخرى في بيرل:

#!/usr/bin/perl

$str = 'mi';
$str =~ m/m[ıi]/ && print "match single normal i\n";

$str = 'mı';
$str =~ m/m[ıi]/ && print "match single undotted ı\n";

$str = 'misir';
$str =~ m/m[ıi]s[ıi]r/ && print "match double normal i\n";

$str = 'mısır';
$str =~ m/m[ıi]s[ıi]r/ && print "match double undotted ı\n";

الاختبارات الثلاثة الأولى تعمل بشكل جيد. آخر واحد لا يتطابق.

لماذا يعمل هذا بشكل جيد كطبقة شخصية مرة واحدة ولكن ليس المرة الثانية في نفس التعبير؟ كيف أكتب تعبيرًا لتتناسب مع كلمة كهذه تحتاج إلى مطابقة بغض النظر عن مجموعات الحروف التي تتم كتابتها؟

يحرر: خلفية على مشكلة اللغة أحاول البرمجة ل.

تحرير 2: إضافة أ use utf8; التوجيه لا يصلح نسخة بيرل. نظرًا لأن مشكلتي الأصلية كانت مع برنامج PHP ولم أتحول إلى Perl فقط لمعرفة ما إذا كان خطأ في PHP ، فهذا لا يساعدني كثيرًا. هل يعرف أي شخص التوجيه لجعل PHP لا يخنق هذا؟

هل كانت مفيدة؟

المحلول

لن تفعل تسلسل Multibyte ما تريده في فصول Char ذات قوسين إذا تم تفسير UTF-8 كتسلسل من بايت 8 بت. فكر في الأمر. إذا [nñm] يتم إساءة فهمه ليس كثلاث أحرف منطقية ولكن كأربعة بايتات فعلية ، ستطابق فقط شخصية نقطة الكود هي 6E أو C3 أو B1 أو 6D.

لبعض الأغراض ، قد تفلت من إعادة الكتابة [nñm] كما (?:n|ñ|m). يعتمد الأمر فقط على ما تفعله. لا تعمل أشياء الغلاف.

أيضا ، يونيكود لديه قواعد غلاف خاصة لأحد الأطراف التركية.

يبدو أن PHP فقط ليست حديثة بما فيه الكفاية. تنهد.

نصائح أخرى

قد تحتاج إلى إخبار Perl أن ملف المصدر الخاص بك يحتوي على أحرف UTF8. محاولة:

#!/usr/bin/perl

use utf8;   # **** Add this line

$str = 'mısır';
$str =~ m/m[ıi]s[ıi]r/ && print "match double undotted ı\n";

وهذا لا يساعدك في PHP ولكن قد يكون هناك توجيه مماثل في PHP. خلاف ذلك ، حاول استخدام شكل من أشكال التسلسل للهروب لتجنب وضع الحرف الحرفي في رمز المصدر الخاص بك. لا أعرف شيئًا عن PHP ، لذا لا يمكنني المساعدة في ذلك.

يحرر
أقرأ أن PHP ليس لديه دعم Unicode. لذلك ، من المحتمل أن يتم التعامل مع إدخال Unicode الذي تمريره على أنه سلسلة من البايتات التي تم تشفيرها على AS.

إذا تمكنت من التأكد من أن مدخلاتك تأتي في UTF-8 ، فيمكنك مطابقة تسلسل UTF-8 ı الذي \xc4 \xb1 كما في:

$str = 'mısır';  # Make sure this source-file is encoded as utf-8 or this match will fail
echo (preg_match('!m(i|\xc4\xb1)s(i|\xc4\xb1)r!', $str)) ? "ok\n" : "fail\n";

هل هذا يعمل؟

تحرير مرة أخرى:
أستطيع أن أشرح لماذا تمر الاختبارات الثلاثة الأولى. دعونا نتظاهر أنه في تشفيرك ، ı هو مشفر كما ABCDE. ثم يرى PHP ما يلي:

echo 'match single normal i: ';
$str = 'mi';
echo (preg_match('!m[ABCDEi]!', $str)) ? "ok\n" : "fail\n";

echo 'match single undotted ABCDE: ';
$str = 'mABCDE';
echo (preg_match('!m[ABCDEi]!', $str)) ? "ok\n" : "fail\n";

echo 'match double normal i: ';
$str = 'misir';
echo (preg_match('!m[ABCDEi]s[ABCDEi]r!', $str)) ? "ok\n" : "fail\n";

echo 'match double undotted ABCDE: ';
$str = 'mABCDEsABCDEr';
echo (preg_match('!m[ABCDEi]s[ABCDEi]r!', $str)) ? "ok\n" : "fail\n";

مما يجعل من الواضح سبب تفشل الاختبارات الثلاثة الأولى وفشل آخر. إذا كنت تستخدم مرساة البداية/النهاية ^...$ أعتقد أنك ستجد أن الاختبار الأول فقط يمر.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top