ค่าผกผันของตัวแก้ไข Perl regex X

ฉันต้องการใช้นิพจน์ทั่วไปของ Perl เพื่อจับคู่สตริงดังนี้:

spaM
s p a m
sp Am
S   p a   m

เมื่อดูตัวดัดแปลง x ของ Perl ฉันควรจะทำสิ่งนี้ได้:

<?php
echo preg_match('#spam#ix', 's p a   m');
?>

แต่สิ่งนี้พิมพ์ออกมา 0 (เท็จ) ตัวแก้ไข x จริงๆ แล้วละเว้นช่องว่างบน regex ไม่ใช่สตริงที่กำลังวิเคราะห์ ฉันจะทำอย่างอื่นได้อย่างไร? นั่นคือละเว้นช่องว่างบนสตริงที่กำลังวิเคราะห์แทนที่จะเป็น regex ของฉัน ฉันทราบว่ามีวิธีหลายขั้นตอนในการทำเช่นนี้ เช่น ขั้นแรกให้ดึงช่องว่างทั้งหมดออกจากสตริง แต่ฉันต้องการทราบว่ามีโซลูชัน regex ขั้นตอนเดียวที่มีประสิทธิภาพหรือไม่


person JoJo    schedule 14.05.2011    source แหล่งที่มา


คำตอบ (3)


โมดิฟายเออร์ #x ทำงานในทางกลับกัน อนุญาตให้ใช้ช่องว่างที่ไม่เกี่ยวข้องใน regex ซึ่งจะถูกละเว้นในการค้นหา:

preg_match('# s p a m #ix')

จะจับคู่เฉพาะ "สแปม" เท่านั้น

สิ่งที่คุณต้องทำเพื่อค้นหาช่องว่างตามอำเภอใจด้วย regex ของคุณคือการแทรก \s* ระหว่างตัวอักษรใดๆ:

preg_match('# S \s* P \s* A \s* M #ix', 's p a   m');

คุณสามารถทำให้เป็นอัตโนมัติ/ลดความซับซ้อนลงเล็กน้อยโดยการแปลงคำให้เป็น regex ที่เหมาะสมโดยผสม \s* โดยใช้:

$regex = join('\s*', str_split("spam", 1));
preg_match("#$regex#ix", "s p a m");
person mario    schedule 14.05.2011
comment
พวกเขาควรประดิษฐ์บางสิ่งเพื่อทำสิ่งนี้ทั้งหมดใน regex ในยุคสมัยของเรา การพัฒนาเว็บไซต์ส่วนใหญ่กำลังต่อสู้กับสแปม - person JoJo; 14.05.2011
comment
@JoJo การรักษารายการคำศัพท์สแปมและการแปลงไม่ใช่งานของกลไก regex แต่เป็นของโมดูล - person ikegami; 15.05.2011
comment
@JoJo มันอาจจะง่ายกว่าที่จะลบช่องว่างออกจากสตริงแทนที่จะรองรับพวกมันในรูปแบบ - person ikegami; 15.05.2011
comment
@อิเคกามิ: นั่นก็จริง นอกจากนี้ยังเป็นสิ่งที่ mod_security ทำ (การทำให้เป็นมาตรฐาน/ถอดรหัสก่อน จากนั้นจึงตรวจสอบ) อย่างไรก็ตามขึ้นอยู่กับบริบท หากคุณต้องการตรวจสอบขอบเขตคำด้วย (ในกรณีนี้ไม่แน่นอน) การลบช่องว่างอาจเป็นอันตรายได้ (เขียนพอร์ต PHP ของการสแปมหนึ่งครั้ง มีข้อดีและข้อเสียมากมายที่นี่) - person mario; 15.05.2011

จริงๆ แล้ว ฉันคิดว่าคุณควรแยกช่องว่างออกแล้วจึงจับคู่กัน เนื่องจากนี่คือสิ่งที่คุณตั้งใจจะทำ โค้ดของคุณจึงชัดเจนกว่าการค้นหา Magic regex หรือการแทรกรูปแบบช่องว่างระหว่างตัวอักษร

Perl สำหรับสิ่งนี้จะมีลักษณะดังนี้

my $string = "S   p A m";
(my $string_no_ws = $string) =~ s/\s//g;
if ($string_no_ws =~ /spam/i) {
  #do something
}

จริงๆ แล้ว คุณสามารถทำการทดสอบโดยไม่ต้องใช้ regex ได้หากต้องการ โดยใช้ index:

my $string = "S   p A m";
(my $lc_string_no_ws = lc $string) =~ s/\s//g;
if (index($lc_string_no_ws, 'spam') >= 0) {
  #do something
}
person Joel Berger    schedule 14.05.2011
comment
นี่เป็นทั้งสะอาดและมีประสิทธิภาพมากกว่าคำตอบอื่น ๆ - person Tim; 14.05.2011

การแก้ไข /x สำหรับ regex ใน Perl อ้างอิงถึงโครงสร้าง regex ไม่ใช่สิ่งที่ถูกจับคู่ เพื่อให้ตรงกับค่าที่คุณต้องการ

/s\s*p\s*a\s*m\s*/i

ถ้าลำดับมีความสำคัญสำหรับคำว่าสแปม และถ้าไม่เป็นเช่นนั้น

/[spam \t\n\r]+/ 

ก็เพียงพอแล้ว

person zellio    schedule 14.05.2011
comment
แผนที่ สปา น้ำยาง และอื่นๆ ไม่ควรถือเป็นคำเดียวกับสแปม! - person ikegami; 15.05.2011
comment
ไม่สิ พวกมันจึงไม่ใช่ความแตกต่างของลำดับที่ไม่สำคัญ - person zellio; 15.05.2011
comment
คุณบอกว่า ถ้า การสั่งซื้อมีความสำคัญ (เน้นของฉัน) ฉันชี้ให้เห็นว่าคำสั่งซื้อ ไม่ มีความสำคัญ - person ikegami; 15.05.2011