سؤال

هذه مشكلة واجهتني مؤخرًا.لدي سلاسل سمات النموذج

"x=1 and y=abc and z=c4g and ..."

تحتوي بعض السمات على قيم رقمية، وبعضها يحتوي على قيم ألفا، وبعضها مختلط، وبعضها يحتوي على تواريخ، وما إلى ذلك.

كل سلسلة هي مفترض امتلاك "x=someval and y=anotherval"في البداية، ولكن البعض لا.لدي ثلاثة أشياء يجب أن أفعلها.

  1. التحقق من صحة السلاسل للتأكد من وجودها x و y.
  2. في الواقع تحليل القيم ل x و y.
  3. احصل على بقية السلسلة.

وبالنظر إلى المثال في الأعلى، فإن هذا من شأنه أن يؤدي إلى المتغيرات التالية:

$x = 1;
$y = "abc";
$remainder = "z=c4g and ..."

سؤالي هو:هل هناك طريقة بسيطة (معقولة) لتحليلها و التحقق من صحة مع تعبير عادي واحد؟أي.:

if ($str =~ /someexpression/)
{
    $x = $1;
    $y = $2;
    $remainder = $3;
}

لاحظ أن السلسلة قد تتكون من فقط x و y صفات.هذه سلسلة صالحة.

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

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

المحلول

أنا لست الأفضل في التعبيرات العادية، ولكن هذا يبدو قريبًا جدًا مما تبحث عنه:

/x=(.+) and y=([^ ]+)( and (.*))?/

باستثناء أنك تستخدم 1 دولارًا و2 دولارًا و4 دولارات.في الاستخدام:

my @strs = ("x=1 and y=abc and z=c4g and w=v4l",
            "x=yes and y=no",
            "z=nox and w=noy");

foreach (@strs) {
    if ($_ =~ /x=(.+) and y=([^ ]+)( and (.*))?/) {
        $x = $1;
        $y = $2;
        $remainder = $4;
        print "x: $x; y: $y; remainder: $remainder\n";
    } else {
        print "Failed.\n";
    }
}

انتاج:

x: 1; y: abc; remainder: z=c4g and w=v4l
x: yes; y: no; remainder: 
Failed.

وهذا بالطبع يترك الكثير من عمليات التحقق من الأخطاء، وأنا لا أعرف كل شيء عن مدخلاتك، ولكن يبدو أن هذا يعمل.

نصائح أخرى

بافتراض أنك تريد أيضًا القيام بشيء ما مع أزواج الاسم = القيمة الأخرى، فهذه هي الطريقة التي سأفعل بها (باستخدام الإصدار 5.10 من Perl):

use 5.10.0;
use strict;
use warnings;

my %hash;
while(
    $string =~ m{
       (?: ^ | \G )    # start of string or previous match
       \s*

       (?<key>   \w+ ) # word characters
       =
       (?<value> \S+ ) # non spaces

       \s*             # get to the start of the next match
       (?: and )?
    }xgi
){
    $hash{$+{key}} = $+{value};
}

# to make sure that x & y exist
die unless exists $hash{x} and exists $hash{y};

في لغة Perl الأقدم (على الأقل لغة Perl 5.6)؛

use strict;
use warnings;

my %hash;
while(
    $string =~ m{
       (?: ^ | \G )   # start of string or previous match
       \s*

       ( \w+ ) = ( \S+ )

       \s*            # get to the start of the next match
       (?: and )?
    }xgi
){
    $hash{$1} = $2;
}

# to make sure that x & y exist
die unless exists $hash{x} and exists $hash{y};

تتمتع هذه بميزة إضافية تتمثل في الاستمرار في العمل إذا كنت بحاجة إلى العمل مع المزيد من البيانات.

كتعديل بسيط إلى حد ما لنسخة رود،

/^x=(.+) and y=([^ ]+)(?: and (.*))?/

سيسمح لك باستخدام $1 و$2 و$3 (الـ ?:يجعلها مجموعة غير ملتقطة)، وسيضمن أن السلسلة تبدأ بـ "x=" بدلاً من السماح بمطابقة "not_x="

إذا كانت لديك معرفة أفضل بقيمتي x وy، فيجب استخدام ذلك لتشديد التعبير العادي بشكل أكبر:

my @strs = ("x=1 and y=abc and z=c4g and w=v4l",
        "x=yes and y=no",
        "z=nox and w=noy",
        "not-x=nox and y=present",
        "x=yes and w='there is no and y=something arg here'");

foreach (@strs) {
    if ($_ =~ /^x=(.+) and y=([^ ]+)(?: and (.*))?/) {
        $x = $1;
        $y = $2;
        $remainder = $3;
        print "x: {$x}; y: {$y}; remainder: {$remainder}\n";
    } else {
        print "$_ Failed.\n";
    }
}

انتاج:

x: {1}; y: {abc}; remainder: {z=c4g and w=v4l}
x: {yes}; y: {no}; remainder: {}
z=nox and w=noy Failed.
not-x=nox and y=present Failed.
x: {yes and w='there is no}; y: {something}; remainder: {}

لاحظ أن الجزء المفقود من الاختبار الأخير يرجع إلى أن الإصدار الحالي من اختبار y لا يتطلب أي مسافات، إذا كان اختبار x له نفس القيود لفشلت هذه السلسلة.

لقد أوصلك Rudd وCebjyre إلى معظم الطريق ولكن لديهما مشاكل معينة:

اقترح رود:

/x=(.+) وy=([^ ]+)( و (.*))?/

قام Cebjyre بتعديله إلى:

/^x=(.+) وy=([^ ]+)(?:و (.*))؟/

الإصدار الثاني أفضل لأنه لن يخلط بين "not_x=foo" و"x=foo" ولكنه سيقبل أشياء مثل "x=foo z=bar y=baz" وتعيين $1 = "foo z=bar" وهو غير مرغوب فيه.

ربما هذا هو ما تبحث عنه:

/^x=(\w+) وy=(\w+)(?:و (.*))؟/

هذا لا يسمح بأي شيء بين خيارات x= و y= والأماكن والمسموحات والاختيارية "و..." والتي ستكون في $3

إليك ما فعلته لحل هذه المشكلة:

($x_str, $y_str, $remainder) = split(/ and /, $str, 3);

if ($x_str !~ /x=(.*)/)
{
    # error
}

$x = $1;

if ($y_str !~ /y=(.*)/)
{
    # error
}

$y = $1;

لقد حذفت بعض عمليات التحقق الإضافية ومعالجة الأخطاء.تعمل هذه التقنية، لكنها ليست موجزة أو جميلة كما كنت أتمنى.آمل أن يكون لدى شخص ما اقتراح أفضل بالنسبة لي.

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