จับคู่กับช่องว่าง แต่ไม่ขึ้นบรรทัดใหม่


277

บางครั้งฉันต้องการจับคู่ช่องว่าง แต่ไม่ขึ้นบรรทัดใหม่

จนถึงตอนนี้ฉันก็หันไป[ \t]ใช้ มีวิธีที่น่าอึดอัดใจน้อยลงหรือไม่?


4
BTW, ตัวละครเหล่านี้นอกจากนี้ยังมี [\r\f]"ช่องว่าง"
Eugene Yarmash

2
@eugeney มีใครยังทำแบบฟอร์มฟีดหรือไม่ (\ f's)
Aran Mulholland

1
@AranMulholland: ทุกคนที่มีเครื่องพิมพ์แบบตัวอักษร เครื่องพิมพ์ส่วนใหญ่มีโหมดตัวอักษรเช่นเดียวกับ PostScript หรือสิ่งที่เรียกว่าอินเทอร์เฟซ Hewlett Packard และเพื่อโยนเพจที่คุณส่งฟีดฟอร์ม
Borodin

1
@Borodin Hewlett Packard เรียกว่า PCL (ภาษาควบคุมเครื่องพิมพ์)
CB_Ron

คำตอบ:


182

รุ่น Perl 5.10 และต่อมาการสนับสนุน บริษัท ย่อยชั้นเรียนตัวอักษรในแนวตั้งและแนวนอน\vและ\hเช่นเดียวกับช่องว่างทั่วไปตัวละครคลาส\s

วิธีการแก้ปัญหาที่สะอาดคือการใช้ช่องว่างในแนวนอน\hตัวละครคลาส สิ่งนี้จะจับคู่แท็บและช่องว่างจากชุด ASCII, พื้นที่ไม่แตกจาก ASCII ที่ขยายออกมาหรืออักขระ Unicode ใด ๆ เหล่านี้

U+0009 CHARACTER TABULATION
U+0020 SPACE
U+00A0 NO-BREAK SPACE (not matched by \s)

U+1680 OGHAM SPACE MARK
U+2000 EN QUAD
U+2001 EM QUAD
U+2002 EN SPACE
U+2003 EM SPACE
U+2004 THREE-PER-EM SPACE
U+2005 FOUR-PER-EM SPACE
U+2006 SIX-PER-EM SPACE
U+2007 FIGURE SPACE
U+2008 PUNCTUATION SPACE
U+2009 THIN SPACE
U+200A HAIR SPACE
U+202F NARROW NO-BREAK SPACE
U+205F MEDIUM MATHEMATICAL SPACE
U+3000 IDEOGRAPHIC SPACE

พื้นที่ตามแนวตั้งรูปแบบ\vจะเป็นประโยชน์น้อย แต่ตรงกับตัวละครเหล่านี้

U+000A LINE FEED
U+000B LINE TABULATION
U+000C FORM FEED
U+000D CARRIAGE RETURN
U+0085 NEXT LINE (not matched by \s)

U+2028 LINE SEPARATOR
U+2029 PARAGRAPH SEPARATOR

มีเจ็ดตัวอักษรช่องว่างในแนวตั้งที่ตรงกันและสิบแปดคนในแนวนอนซึ่งการแข่งขัน\v ตรงกับตัวละครยี่สิบสาม\h\s

อักขระช่องว่างทั้งหมดเป็นแนวตั้งหรือแนวนอนโดยไม่ทับซ้อนกัน แต่เป็นเซตย่อยที่ไม่เหมาะสมเพราะ\hตรงกับ U + 00A0 NO-BREAK SPACE และ\vตรงกับ U + 0085 NEXT LINE ซึ่งไม่ตรงกับ\s


7
\hใช้งานได้กับภาษาที่รองรับPCREเท่านั้น
Avinash Raj

14
@AvinashRaj: คำถามนี้เป็นเรื่องเกี่ยวกับ Perl ซึ่งแน่นอนสนับสนุนพีซีรี
Borodin

2
@AvinashRaj: ยกเว้นที่[[:blank:]]ไม่ตรงกับที่ไม่มีช่องว่าง -  หรือ"\xA0"
Borodin

6
ต้องการพูดถึงว่า\hทำงานได้อย่างสมบูรณ์แบบสำหรับกรณีการใช้งานของฉันซึ่งทำการค้นหา / แทนที่ใน Notepad ++ บนพื้นที่ว่างที่ไม่ใช่บรรทัดใหม่ที่อยู่ติดกันอย่างน้อย 1 บรรทัด ไม่มีอะไรทำงาน (ง่าย)
squidbe

8
สิ่งที่ทำให้ Perl \hไม่ได้มาตรฐานเล็กน้อยคือการรวมเข้าไว้MONGOLIAN VOWEL SEPARATORด้วย Unicode ไม่ถือว่าเป็นช่องว่าง สำหรับเหตุผลที่ Perl \hแตกต่างจาก POSIX blank( [[:blank:]]ใน Perl, \p{Blank}ในชวา) และ Java \h8 เป็นที่ยอมรับว่าเป็นกรณีขอบ
Aleksandr Dubinsky

362

ใช้ลบสองครั้ง:

/[^\S\r\n]/

นั่นคือไม่ใช่ช่องว่าง (ตัวเสริม S) หรือไม่ใช่สายการบินส่งคืนหรือไม่ขึ้นบรรทัดใหม่ การกระจายด้านนอกไม่ใช่ ( เช่นการเติมเต็ม^ในคลาสอักขระ) ด้วยกฎของเดมอร์แกนนี่เทียบเท่ากับ“ ช่องว่าง แต่ไม่ใช่สายการบินส่งคืนหรือขึ้นบรรทัดใหม่” การรวมทั้ง\rและ\nในรูปแบบถูกต้องจัดการ Unix (LF) คลาสสิก Mac OS (CR) และ DOS-ish (CR LF) ข้อตกลงใหม่ทั้งหมด

ไม่จำเป็นต้องใช้คำพูดของฉันมัน:

#! /usr/bin/env perl

use strict;
use warnings;

use 5.005;  # for qr//

my $ws_not_crlf = qr/[^\S\r\n]/;

for (' ', '\f', '\t', '\r', '\n') {
  my $qq = qq["$_"];
  printf "%-4s => %s\n", $qq,
    (eval $qq) =~ $ws_not_crlf ? "match" : "no match";
}

เอาท์พุท:

"" => จับคู่
"\ f" => จับคู่
"\ t" => จับคู่
"\ r" => ไม่ตรงกัน
"\ n" => ไม่ตรงกัน

หมายเหตุยกเว้นของแท็บแนวตั้ง แต่นี่คือการแก้ไขใน v5.18

ก่อนที่จะคัดค้านอย่างรุนแรงเกินไปเอกสารประกอบ Perl ใช้เทคนิคเดียวกัน เชิงอรรถในส่วน"ช่องว่าง" ของ perlrecharclassอ่าน

ก่อนหน้า Perl v5.18 \sไม่ตรงกับแท็บแนวตั้ง [^\S\cK](คลุมเครือ) ตรงกับสิ่งที่\sทำตามธรรมเนียม

ส่วนที่เหมือนกันของ perlrecharclassยังแนะนำวิธีการอื่น ๆ ที่จะไม่ขัดต่อความขัดแย้งของครูผู้สอนภาษาต่อการลบสองชั้น

นอกกฎโลแคลและ Unicode หรือเมื่อ/aสวิตช์มีผลบังคับใช้“ \sจับคู่[\t\n\f\r ]และเริ่มต้นใน Perl v5.18 แท็บแนวตั้ง\cK” ยกเลิก\rและ\nออกจาก/[\t\f\cK ]/การจับคู่ของช่องว่าง แต่ไม่ขึ้นบรรทัดใหม่

ถ้าข้อความของคุณเป็น Unicode ใช้รหัสคล้ายกับการย่อยด้านล่างเพื่อสร้างรูปแบบจากตารางในส่วนเอกสารดังกล่าวข้างต้น

sub ws_not_nl {
  local($_) = <<'EOTable';
0x0009        CHARACTER TABULATION   h s
0x000a              LINE FEED (LF)    vs
0x000b             LINE TABULATION    vs  [1]
0x000c              FORM FEED (FF)    vs
0x000d        CARRIAGE RETURN (CR)    vs
0x0020                       SPACE   h s
0x0085             NEXT LINE (NEL)    vs  [2]
0x00a0              NO-BREAK SPACE   h s  [2]
0x1680            OGHAM SPACE MARK   h s
0x2000                     EN QUAD   h s
0x2001                     EM QUAD   h s
0x2002                    EN SPACE   h s
0x2003                    EM SPACE   h s
0x2004          THREE-PER-EM SPACE   h s
0x2005           FOUR-PER-EM SPACE   h s
0x2006            SIX-PER-EM SPACE   h s
0x2007                FIGURE SPACE   h s
0x2008           PUNCTUATION SPACE   h s
0x2009                  THIN SPACE   h s
0x200a                  HAIR SPACE   h s
0x2028              LINE SEPARATOR    vs
0x2029         PARAGRAPH SEPARATOR    vs
0x202f       NARROW NO-BREAK SPACE   h s
0x205f   MEDIUM MATHEMATICAL SPACE   h s
0x3000           IDEOGRAPHIC SPACE   h s
EOTable

  my $class;
  while (/^0x([0-9a-f]{4})\s+([A-Z\s]+)/mg) {
    my($hex,$name) = ($1,$2);
    next if $name =~ /\b(?:CR|NL|NEL|SEPARATOR)\b/;
    $class .= "\\N{U+$hex}";
  }

  qr/[$class]/u;
}

แอปพลิเคชันอื่น ๆ

เคล็ดลับการลบสองครั้งยังมีประโยชน์สำหรับการจับคู่อักขระที่เป็นตัวอักษรเช่นกัน จำไว้ว่า\wตรงกับ“ ตัวอักษรคำ” ตัวอักษรและตัวเลขและขีดเส้นใต้ เราน่าเกลียดคนอเมริกันบางครั้งต้องการเขียนเป็นพูด

if (/[A-Za-z]+/) { ... }

แต่คลาสอักขระที่มีค่าลบสองเท่าสามารถเคารพโลแคลได้:

if (/[^\W\d_]+/) { ... }

การแสดง“ คำตัวอักษร แต่ไม่ใช่ตัวเลขหรือขีดเส้นใต้” วิธีนี้เป็นบิตทึบแสง คลาสอักขระ POSIX สื่อสารจุดประสงค์เพิ่มเติมโดยตรง

if (/[[:alpha:]]+/) { ... }

หรือด้วยคุณสมบัติ Unicode ตามที่แนะนำszbalint

if (/\p{Letter}+/) { ... }

4
ฉลาด แต่พฤติกรรมนั้นน่าประหลาดใจมากและฉันไม่เห็นว่ามันจะน่าอึดอัดน้อยไปกว่านี้
Qwertie

7
@Qwertie: มีอะไรน่าแปลกใจ? น้อยกว่าที่น่าอึดอัดใจกว่าอะไร
ysth

9
น่ากลัวมาก

9
นี่เป็นสิ่งที่ดีมาก ตามที่ร้องขอคุณจับคู่กับช่องว่าง (ไม่ใช่เพียงบางตัวของช่องว่าง) และคุณไม่รวมอักขระตัวดึงบรรทัด วิธีแก้ปัญหาของคุณไม่เกี่ยวข้องกับคำถาม: "มีอักขระช่องว่างใดบ้าง" ตามที่ควรจะเป็น นี่คือสิ่งที่ฉันกำลังมองหา (ตามที่ระบุไว้โดย @Rory 'newline' อาจรวมถึง\rเช่นใน Windows ดังนั้นให้พิจารณาการยกเว้นเหล่านั้นจากการแข่งขันด้วยเช่นกัน/[^\S\r\n]/)
Timo

1
สิ่งนี้จะตอบสนองความต้องการของ OP และแน่นอนว่าทุกคนที่ค้นหาคำถามนี้ (ผู้พูดภาษาอังกฤษ) แต่มันก็ยังเป็นคำตอบที่ไม่ดี ไม่มีข้อแก้ตัวใด ๆ สำหรับการใช้โซลูชันนี้เมื่อ\hพร้อมใช้งาน
อลันมัวร์

50

ความหลากหลายของคำตอบของ Gregซึ่งรวมถึง carriage return ด้วย:

/[^\S\r\n]/

regex นี้ปลอดภัยกว่าไม่มี/[^\S\n]/ \rเหตุผลของฉันคือการที่ Windows ใช้\r\nสำหรับการขึ้นบรรทัดใหม่และ Mac OS 9 \rใช้ คุณไม่น่าจะพบ\rได้\nทุกวันนี้ แต่ถ้าคุณพบมันก็ไม่ได้แปลความหมายอะไรเลยนอกจากบรรทัดใหม่ ดังนั้นเนื่องจาก\rอาจหมายถึงการขึ้นบรรทัดใหม่เราจึงควรยกเว้นเช่นกัน


1
+1 โซลูชันของ Gregจบลงด้วยการทำลายข้อความของฉันคุณทำงานได้ดี
Timo Huovinen

คุณอาจประหลาดใจกับจำนวนโปรแกรมที่ยังคงใช้ "\ r" สำหรับการสิ้นสุดบรรทัด บางครั้งฉันก็ต้องใช้เวลาพอสมควรที่จะเข้าใจว่าปัญหาของฉันคือไฟล์ใช้สิ่งเหล่านี้ หรือว่ามันใช้การเข้ารหัสตัวอักษร MacRoman ...
mivk

2
ดูเหมือน @Greg ครั้งแรกหากมีการ "ผิด" เปลี่ยนไปและไม่ได้ให้เครดิตแก่คุณ นั่นเป็นเหตุผลที่ฉัน upvoting ที่นี่
Andre Elrico

14

regex ด้านล่างจะจับคู่ช่องว่างสีขาว แต่ไม่ใช่อักขระบรรทัดใหม่

(?:(?!\n)\s)

การสาธิต

หากคุณต้องการเพิ่มการขึ้นบรรทัดใหม่ให้เพิ่ม\rด้วยตัว|ดำเนินการภายใน lookahead เชิงลบ

(?:(?![\n\r])\s)

การสาธิต

เพิ่ม+หลังจากกลุ่มที่ไม่ได้จับภาพเพื่อจับคู่ช่องว่างสีขาวหนึ่งช่องหรือมากกว่า

(?:(?![\n\r])\s)+

การสาธิต

ฉันไม่รู้ว่าทำไมคนคุณถึงไม่พูดถึงคลาสตัวอักษร POSIX [[:blank:]]ที่ตรงกับช่องว่างแนวนอนใด ๆ ( ช่องว่างและแท็บ ) คลาส chracter ของ POSIX นี้จะทำงานกับ BRE ( นิพจน์เรขาพื้นฐาน ), ERE ( นิพจน์ทั่วไปแบบขยาย ), PCRE ( Perl Compatible Regular Expression )

การสาธิต


นี่คือทางออกที่ดีที่สุด!
loretoparisi

13

สิ่งที่คุณกำลังค้นหาคือblankคลาสอักขระPOSIX ใน Perl มันถูกอ้างอิงเป็น:

[[:blank:]]

ใน Java (อย่าลืมเปิดใช้งานUNICODE_CHARACTER_CLASS):

\p{Blank}

เมื่อเทียบกับที่คล้ายกัน\hPOSIX blankได้รับการสนับสนุนโดยเอ็นจิน regex เพิ่มเติมอีกสองสามตัว ( อ้างอิง ) ประโยชน์ที่สำคัญคือคำจำกัดความถูกแก้ไขในภาคผนวก C: คุณสมบัติความเข้ากันได้ของ Unicode การแสดงออกปกติและมาตรฐานในทุกรสชาติของ regex ที่สนับสนุน Unicode (ตัวอย่างเช่นใน Perl \hเลือกที่จะใส่เครื่องหมายMONGOLIAN VOWEL SEPARATORเพิ่มเติม) อย่างไรก็ตามอาร์กิวเมนต์ที่สนับสนุน\hก็คือมันจะตรวจจับอักขระ Unicode เสมอ (แม้ว่าเอนจินจะไม่เห็นด้วยกับที่) ในขณะที่คลาส POSIX มักจะเป็น ASCII - เท่านั้น (เช่นใน Java)

แต่ปัญหาคือแม้การเกาะกับ Unicode จะไม่สามารถแก้ปัญหาได้ 100% พิจารณาตัวละครต่อไปนี้ซึ่งไม่ถือว่าเป็นช่องว่างใน Unicode:

  • ตัวแยกสระมองโกเลีย + 180E

  • U + 200B ZERO WIDTH SPACE

  • U + 200C ศูนย์กว้างไม่เข้าร่วม

  • U + 200D ZERO WIDTH JOINER

  • U + 2060 WORD JOINER

  • U + FEFF ZERO กับพื้นที่ว่างเปล่า

    นำมาจากhttps://en.wikipedia.org/wiki/White-space_character

ตัวคั่นสระมองโกเลียดังกล่าวไม่รวมอยู่ในสิ่งที่อาจเป็นเหตุผลที่ดี มันพร้อมกับ 200C และ 200D เกิดขึ้นภายในคำ (AFAIK) และดังนั้นจึงแบ่งกฎสำคัญที่ช่องว่างอื่น ๆ ทั้งหมดเชื่อฟัง: คุณสามารถโทเค็นกับมัน พวกมันเป็นเหมือนตัวดัดแปลง อย่างไรก็ตามZERO WIDTH SPACE, WORD JOINERและZERO WIDTH NON-BREAKING SPACE(ถ้าใช้เป็นอื่นที่ไม่ใช่เครื่องหมายสั่งไบต์) พอดีกับกฎช่องว่างในหนังสือของฉัน ดังนั้นฉันรวมไว้ในคลาสอักขระช่องว่างแนวนอน

ใน Java:

static public final String HORIZONTAL_WHITESPACE = "[\\p{Blank}\\u200B\\u2060\\uFFEF]"

คุณต้องเพิ่มแฟล็กคอมไพล์ regexp ที่เหมาะสมในการคอมไพล์ Java และรัน Java 7 หรือใหม่กว่า ไม่ว่าในกรณีใด ๆ คำถามนี้ไม่เกี่ยวกับ Java หรือ PCRE เลยนี่เป็นสิ่งสำคัญ
tchrist

@tchrist ขอบคุณที่ชี้ให้เห็น ฉันจะอัปเดตคำตอบของฉัน ฉันไม่เห็นด้วยแม้ว่าคำตอบของฉันไม่เกี่ยวข้อง perlแท็กในคำถามเดิมไม่มีสาระสำคัญอะไร
Aleksandr Dubinsky

1
@AleksandrDubinsky, \ p {ว่าง} ไม่ได้รับการสนับสนุนใน JavaScript ดังนั้นไม่แน่นอน "มาตรฐานของทุกรสชาติ regex" -1
Valentin Vasilyev

ข้อมูลมากที่สุด ฉันพบว่ามันน่ารำคาญที่ได้รู้ว่าตัวละครชวเลขว่าง "แนวนอนในช่องว่าง" ทั่วไปและสมบูรณ์ไม่มีอยู่จริงและ[\p{Blank}\u200b\u180e]ต้องมีความน่ากลัวเช่นนั้น เป็นที่ยอมรับกันแล้วว่ารู้สึกว่าตัวแยกสระไม่ถือว่าเป็นอักขระช่องว่าง แต่ทำไมพื้นที่ว่างที่มีความกว้างเป็นศูนย์ไม่ได้อยู่ในคลาสที่ชอบ\sและ\p{Blank}ทำให้ฉันเต้น
ติโม

การติดตามผล: ผมอ่านที่ทั้งสองได้รับการพิจารณา 'เขตแดนเป็นกลาง' ถึงแม้ว่ามันจะไม่ได้อธิบายว่าทำไม
ติโม

-4

m/ /gเพียงแค่ให้พื้นที่ใน/ /และมันจะทำงาน หรือใช้\S- มันจะแทนที่อักขระพิเศษทั้งหมดเช่นแท็บบรรทัดใหม่ช่องว่างและอื่น ๆ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.