كيف يمكنني استخدام بيرل لنثر الحروف بين مباريات متتالية مع إجراء تبديل رجإكس؟

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

  •  22-07-2019
  •  | 
  •  

سؤال

والأسطر التالية من قيم مفصولة بفواصل تحتوي على عدة حقول فارغة متتالية:

$rawData = 
"2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,Clear\n
2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,,,\n"

وأريد أن تحل محل هذه الحقول الفارغة مع 'N / A' القيم، والذي هو السبب في أنني قررت أن تفعل ذلك عن طريق إجراء تبديل رجإكس.

وحاولت هذه قبل كل شيء:

$rawdata =~ s/,([,\n])/,N\/A/g; # RELABEL UNAVAILABLE DATA AS 'N/A'

والذي عاد

2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,N/A,Clear\n
2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,N/A,,N/A,\n

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

وأعتقد أن هذا يمكن أن يكون أن تفعل شيئا مع lookahead مقابل تأكيدات مراجعة الماضي، لذلك حاولت التعبير المعتاد الآتية:

$rawdata =~ s/(?<=,)([,\n])|,([,\n])$/,N\/A$1/g; # RELABEL UNAVAILABLE DATA AS 'N/A'

والتي أسفرت عن:

2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,N/A,Clear\n
2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,N/A,,N/A,,N/A,,N/A\n

وهذا لم ينجح أيضا. انها تحولت مجرد فاصلة-حدودا من جانب واحد.

وأنا أعلم أن غسل هذه السلسلة خلال نفس التعابير المنطقية مرتين وسوف نفعل ذلك، ولكن يبدو أن النفط الخام. بالتأكيد، يجب أن يكون هناك طريقة للحصول على استبدال باستخدام التعابير المنطقية واحد للقيام بهذه المهمة. أي اقتراحات؟

والسلسلة النهائية يجب أن تبدو هذه:

2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,N/A,N/A,Clear\n
2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,N/A,,N/A,N/A,N/A,N/A,N/A\n
هل كانت مفيدة؟

المحلول

وأنا لا يمكن أن تجعل تماما ما كنت تحاول القيام به في المثال lookbehind الخاص بك، ولكن أظن أنك تعاني من خطأ الأسبقية هناك، ويجب أن يرفق أن كل شيء بعد lookbehind في (?: ... ) ذلك | لا توجد الآن ' ر تجنب القيام lookbehind.

وتبدأ من الصفر، ما كنت تحاول أن تفعل يبدو بسيطا جدا: مكان N / A بعد فاصلة إذا كان متبوعا فاصلة أخرى أو سطر جديد:

s!,(?=[,\n])!,N/A!g;

مثال:

my $rawData = "2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,Clear\n2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,,,\n";

use Data::Dumper;
$Data::Dumper::Useqq = $Data::Dumper::Terse = 1;
print Dumper($rawData);
$rawData =~ s!,(?=[,\n])!,N/A!g;
print Dumper($rawData);

وإخراج:

"2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,Clear\n2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,,,\n"
"2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,N/A,Clear\n2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,N/A,N/A,N/A,N/A\n"

نصائح أخرى

وتحرير: لاحظ أنه يمكن فتح filehandle إلى سلسلة البيانات والسماح صفقة readline مع نهايات السطر:

#!/usr/bin/perl

use strict; use warnings;
use autodie;

my $str = <<EO_DATA;
2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,Clear
2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,,,
EO_DATA

open my $str_h, '<', \$str;

while(my $row = <$str_h>) {
    chomp $row;
    print join(',',
        map { length $_ ? $_ : 'N/A'} split /,/, $row, -1
    ), "\n";
}

وإخراج:

E:\Home> t.pl
2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,N/A,Clear
2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,N/A,N/A,N/A,N/A

ويمكنك أيضا استخدام:

pos $str -= 1 while $str =~ s{,(,|\n)}{,N/A$1}g;

شرح: عندما يجد s/// على ,, ويستبدلها مع ,N/A, أنها انتقلت بالفعل إلى الحرف بعد الفاصلة الماضية. لذلك، فإنه سوف يغيب عن بعض الفواصل متتالية إذا كنت تستخدم فقط

$str =~ s{,(,|\n)}{,N/A$1}g;

لذلك، وأنا استخدم حلقة لنقل pos $str الظهر بواسطة حرف بعد استبدال كل ناجح.

والآن، و<لأ href = "https://stackoverflow.com/questions/1646137/how-do-i-use-perl-to-intersperse-characters-between-consecutive-matches-with-a-re / 1646252 # 1646252 "> @ ysth يظهر :

$str =~ s!,(?=[,\n])!,N/A!g;

ومن شأنه أن يجعل while لا لزوم لها.

هل يمكن البحث عن

(?<=,)(?=,|$)

ووالاستعاضة عن ذلك مع N / A.

وهذه التعابير المنطقية يطابق (فارغة) مسافة بين اثنين من الفواصل أو بين فاصلة ونهاية الخط.

ووسريعة وقذرة نسخة الإختراق:

my $rawData = "2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,Clear
2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,,,\n";
while ($rawData =~ s/,,/,N\/A,/g) {};
print $rawData;

وليس رمز أسرع، ولكن أقصر. كما ينبغي حلقة من خلال كحد أقصى مرتين.

وليس باستخدام التعابير المنطقية، ولكن ليست معقدة للغاية سواء:

$string = join ",", map{$_ eq "" ? "N/A" : $_} split (/,/, $string,-1);

مطلوب ,-1 في نهاية لإجبار split لتشمل أي حقول فارغة في نهاية السلسلة.

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