Parsing atribut dengan regex di Perl

Berikut ini's masalah aku berlari ke baru-baru ini. Saya memiliki atribut string bentuk

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

Beberapa atribut yang memiliki nilai-nilai numerik, beberapa memiliki nilai alpha, beberapa telah dicampur, beberapa memiliki tanggal, dll.

Setiap string seharusnya memiliki "x=someval dan y=anotherval" di awal, tetapi beberapa don't. Saya memiliki tiga hal yang perlu saya lakukan.

  1. Memvalidasi string untuk memastikan bahwa mereka memiliki x dan y.
  2. Benar-benar mengurai nilai-nilai x dan y.
  3. Mendapatkan sisa dari string.

Diberikan contoh di atas, hal ini akan mengakibatkan variabel-variabel berikut:

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

Pertanyaan saya adalah: Apakah ada (cukup) cara sederhana untuk mengurai ini dan memvalidasi dengan satu ekspresi reguler? yaitu:

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

Perhatikan bahwa string dapat terdiri dari hanya x dan y atribut. Ini adalah valid string.

I'll posting saya solusi sebagai jawaban, tapi itu doesn't bertemu saya satu-regex preferensi.

Dengan asumsi anda juga ingin melakukan sesuatu dengan nama lain=nilai pasangan ini adalah bagaimana saya akan melakukannya ( menggunakan Perl versi 5.10 ):

use 5.10.0;
use strict;
use warnings;

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

       (?   \w+ ) # word characters
       =
       (? \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};

Pada yang lebih tua Perls ( setidaknya 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};

Ini memiliki manfaat tambahan untuk terus bekerja jika anda perlu untuk bekerja dengan data yang lebih banyak.

Komentar (2)
Larutan

I'm bukan yang terbaik pada ekspresi reguler, tapi ini tampaknya cukup dekat dengan apa yang anda'kembali mencari:

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

Kecuali anda menggunakan $1, $2, dan $4. Di gunakan:

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";
    }
}

Output:

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

Hal ini tentu saja meninggalkan banyak memeriksa kesalahan, dan saya don't tahu segala sesuatu tentang anda input, tetapi hal ini tampaknya bekerja.

Komentar (0)

Sebagai seorang yang cukup sederhana modifikasi Rudd's versi,

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

akan memungkinkan anda untuk menggunakan $1, $2 dan $3 (?: membuat kelompok noncapturing), dan akan memastikan bahwa string dimulai dengan "x=" daripada membiarkan "not_x=" untuk pertandingan

Jika anda memiliki pengetahuan yang lebih baik dari apa yang nilai x dan y akan, ini harus digunakan untuk mengencangkan regex lebih lanjut:

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";
    }
}

Output:

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: {}

Perhatikan bahwa bagian yang hilang dari tes terakhir adalah karena saat ini versi y tes yang membutuhkan tanpa spasi, jika x uji yang sama-sama memiliki pembatasan string yang akan gagal.

Komentar (0)

Rudd dan Cebjyre telah mendapat anda sebagian besar jalan di sana tapi mereka berdua memiliki masalah tertentu:

Rudd yang disarankan:

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

Cebjyre dimodifikasi untuk:

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

Versi kedua lebih baik karena tidak akan bingung "not_x=foo" dengan "x=foo" tapi akan menerima hal-hal seperti "x=foo z=bar y=baz" dan mengatur $1 = "foo z=bar" yang tidak diinginkan.

Ini mungkin adalah apa yang anda cari adalah:

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

Ini melarang apa-apa antara x= dan y= pilihan, tempat dan memungkinkan dan opsional " dan..." yang akan di $3

Komentar (0)

Berikut ini's pada dasarnya apa yang saya lakukan untuk memecahkan masalah ini:

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

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

$x = $1;

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

$y = $1;

I've dihilangkan beberapa tambahan validasi dan penanganan kesalahan. Teknik ini bekerja, tapi itu's tidak seperti yang singkat atau cukup seperti yang saya akan senang. I'm berharap seseorang akan memiliki saran yang lebih baik untuk saya.

Komentar (1)