ยุบลำดับของช่องว่างเป็นอักขระเดี่ยวและสตริงตัดแต่ง


122

พิจารณาตัวอย่างต่อไปนี้:

"    Hello      this  is a   long       string!   "

ฉันต้องการแปลงเป็น:

"Hello this is a long string!"

คำตอบ:


125

OS X 10.7+ และ iOS 3.2+

ใช้โซลูชัน regexpดั้งเดิมที่จัดทำโดย hfossli

มิฉะนั้น

ใช้ไลบรารี regexp ที่คุณชื่นชอบหรือใช้ Cocoa-native solution ต่อไปนี้:

NSString *theString = @"    Hello      this  is a   long       string!   ";

NSCharacterSet *whitespaces = [NSCharacterSet whitespaceCharacterSet];
NSPredicate *noEmptyStrings = [NSPredicate predicateWithFormat:@"SELF != ''"];

NSArray *parts = [theString componentsSeparatedByCharactersInSet:whitespaces];
NSArray *filteredArray = [parts filteredArrayUsingPredicate:noEmptyStrings];
theString = [filteredArray componentsJoinedByString:@" "];

4
ฉันอยากรู้เกี่ยวกับการเปรียบเทียบประสิทธิภาพของสิ่งนี้กับการแทนที่ regex ด้วยการตัดแต่งเพื่อลบส่วนท้าย ในแง่หนึ่งคุณมี regex ที่ต้องจัดการ ในทางกลับกันคุณมีเพรดิเคต ต้องการการประมวลผลภายในของนิพจน์ที่เกี่ยวข้อง
lilbyrdie

@lilbyrdie: ขึ้นอยู่กับสตริงที่ฉันคิดว่ามีช่องว่างกี่ช่อง วิธีแก้ปัญหาของฉันค่อนข้างช้าเพราะสร้างออบเจ็กต์ใหม่สำหรับทุกสตริงย่อยและส่งการเรียกเมธอดไปยังแต่ละรายการ
Georg Schölly

2
คำตอบที่ดีได้รับการโหวตให้เป็นเช่นนี้ แต่ฉันท้าทายคำจำกัดความของคุณว่า "ง่าย" ขอแสดงความนับถืออดีต Python Guy ตอนนี้อยู่ที่ ObjC-land ;-)
JK Laiho

2
คุณทำให้ฉันหัวเราะด้วย "อย่าใช้วิธีแก้ปัญหาที่ซับซ้อนถ้ามีวิธีง่ายๆ" วิธีที่ง่ายที่สุดคือ [toBeTrimmed stringByReplacingOccurrencesOfString: @ "" withString: @ ""] ไม่? ฉันยังคงโหวตให้คำตอบของคุณ แต่มันง่ายที่สุดแน่นอน
Mário Carvalho

2
@ MárioCarvalhoคำถามถามว่าจะลบช่องว่างส่วนเกินได้อย่างไรไม่ใช่ทั้งหมด
swilliams

52

Regex และ NSCharacterSet พร้อมให้ความช่วยเหลือคุณ โซลูชันนี้จะตัดช่องว่างที่นำหน้าและต่อท้ายตลอดจนช่องว่างหลายช่อง

NSString *original = @"    Hello      this  is a   long       string!   ";

NSString *squashed = [original stringByReplacingOccurrencesOfString:@"[ ]+"
                                                         withString:@" "
                                                            options:NSRegularExpressionSearch
                                                              range:NSMakeRange(0, original.length)];

NSString *final = [squashed stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

การบันทึกfinalให้

"Hello this is a long string!"

รูปแบบ regex ทางเลือกที่เป็นไปได้:

  • แทนที่เฉพาะช่องว่าง: [ ]+
  • แทนที่ช่องว่างและแท็บ: [ \\t]+
  • แทนที่ช่องว่างแท็บและบรรทัดใหม่: \\s+

สรุปประสิทธิภาพ

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


3
hfossli เป็นคำตอบที่สวยหรูที่สุดในหนังสือของฉัน นอกจากนี้ฉันเพิ่งเรียนรู้ว่าคุณสามารถใช้นิพจน์ทั่วไปในstringByReplacingOccurrencesOfString:. ไม่อยากจะเชื่อเลยว่าฉันไม่รู้
davidf2281

1
น่ากลัว ทำงานอย่างมีเสน่ห์
Kushal Ashok

41

จริงๆแล้วมีวิธีง่ายๆในการแก้ไข:

NSString *string = @" spaces in front and at the end ";
NSString *trimmedString = [string stringByTrimmingCharactersInSet:
                                  [NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSLog(@"%@", trimmedString)

( ที่มา )


29
ฉันคิดว่าสิ่งนี้จะกำจัดเฉพาะช่องว่างนำหน้าและต่อท้ายและกำจัดทั้งหมด มันจะไม่จัดการกับ "hello foo"
Brian Postow

2
d * mn การลงท้ายบรรทัดและรูปแบบอัตโนมัติ ... มันไม่เกี่ยวข้องกับ "hello______foo" (สมมติว่า _ -> "" เพราะการจัดรูปแบบความคิดเห็นทำได้ยาก)
Brian Postow

32
ทำไมคุณถึงมีคนโหวตให้และคำตอบที่ไม่มีคำตอบสำหรับคำถาม? stringByTrimmingCharactersInSet ไม่ได้วิเคราะห์หรือวิเคราะห์ iside ของสตริง แต่เป็นขอบเท่านั้น คำตอบโดย Georg Sholly เป็นสิ่งที่สมบูรณ์แบบ
Lukasz

3
ไม่ใช่คำตอบสำหรับคำถาม แต่แน่นอนว่าช่วยฉันได้ ขอบคุณ
daveMac

1
รหัสที่ยอดเยี่ยมสำหรับการลบช่องว่างด้านหน้าและด้านหลังในเวลาเดียวกัน
user523234

13

ด้วย regex แต่ไม่จำเป็นต้องใช้กรอบภายนอกใด ๆ :

NSString *theString = @"    Hello      this  is a   long       string!   ";

theString = [theString stringByReplacingOccurrencesOfString:@" +" withString:@" "
                       options:NSRegularExpressionSearch
                       range:NSMakeRange(0, theString.length)];

จากนั้นคุณยังต้องตัดแต่งผลลัพธ์ไม่เช่นนั้นคุณจะถูกบุด้วยช่องว่าง นี่อาจเป็นคำตอบที่ง่ายที่สุด
lilbyrdie

2
เอกสารประกอบNSRegularExpressionSearchบอกว่าใช้ได้กับrangeOfString:...วิธีการเท่านั้น
user102008

9

โซลูชันหนึ่งบรรทัด:

NSString *whitespaceString = @" String with whitespaces ";

NSString *trimmedString = [whitespaceString
        stringByReplacingOccurrencesOfString:@" " withString:@""];

2
ช่วยฉันที :) ขอบคุณสำหรับสิ่งนั้น!
thedom

5
แม้ว่าจะมีประโยชน์ แต่ก็ลบช่องว่างทั้งหมด OP โดยทั่วไปต้องการการบดอัดช่องว่างเช่นการตัดแต่งตามด้วยการลดช่องว่างที่ต่อเนื่องกันเป็นช่องว่างเดียว
lilbyrdie

หมายเหตุอีกประการหนึ่งโซลูชันนี้ไม่เกี่ยวข้องกับแท็บหรือขึ้นบรรทัดใหม่หรืออักขระเว้นวรรคนอกเหนือจากช่องว่าง
fwielstra

2
สิ่งนี้ไม่ตอบโจทย์ OP แต่จะลบช่องว่างทั้งหมดในสตริงแทนดังนั้นคุณจึงลงเอยด้วย @ "Stringwithwhitespaces"
charles

6

สิ่งนี้ควรทำ ...

NSString *s = @"this is    a  string    with lots  of     white space";
NSArray *comps = [s componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];

NSMutableArray *words = [NSMutableArray array];
for(NSString *comp in comps) {
  if([comp length] > 1)) {
    [words addObject:comp];
  }
}

NSString *result = [words componentsJoinedByString:@" "];

1
สิ่งนี้ใช้ได้กับสตริง 'a' หรือไม่? มันมีความยาว 1 เท่าที่ฉันเห็นวิธีนี้จะกรองคำที่แยกออกทั้งหมดที่มีขนาด 0 และ 1
fwielstra

ใช่นั่นคือคำตอบที่ฉันคาดหวัง ขอบคุณ +1
पवन


3

ลองสิ่งนี้

NSString *theString = @"    Hello      this  is a   long       string!   ";

while ([theString rangeOfString:@"  "].location != NSNotFound) {
    theString = [theString stringByReplacingOccurrencesOfString:@"  " withString:@" "];
}

3

นี่คือตัวอย่างจากNSStringส่วนขยายที่"self"เป็นNSStringตัวอย่าง สามารถใช้เพื่อยุบช่องว่างที่ต่อเนื่องกันให้เป็นช่องว่างเดียวโดยการส่งผ่าน[NSCharacterSet whitespaceAndNewlineCharacterSet]และ' 'ไปยังอาร์กิวเมนต์ทั้งสอง

- (NSString *) stringCollapsingCharacterSet: (NSCharacterSet *) characterSet toCharacter: (unichar) ch {
int fullLength = [self length];
int length = 0;
unichar *newString = malloc(sizeof(unichar) * (fullLength + 1));

BOOL isInCharset = NO;
for (int i = 0; i < fullLength; i++) {
    unichar thisChar = [self characterAtIndex: i];

    if ([characterSet characterIsMember: thisChar]) {
        isInCharset = YES;
    }
    else {
        if (isInCharset) {
            newString[length++] = ch;
        }

        newString[length++] = thisChar;
        isInCharset = NO;
    }
}

newString[length] = '\0';

NSString *result = [NSString stringWithCharacters: newString length: length];

free(newString);

return result;
}

-1

ทางเลือกอื่น: รับสำเนา OgreKit (ไลบรารีนิพจน์ทั่วไปของ Cocoa)

  • OgreKit (หน้าเว็บภาษาญี่ปุ่น - รหัสเป็นภาษาอังกฤษ)
  • OgreKit (การแปลอัตโนมัติของ Google):

จากนั้นฟังก์ชั่นทั้งหมดคือ:

NSString *theStringTrimmed =
   [theString stringByTrimmingCharactersInSet:
        [NSCharacterSet whitespaceAndNewlineCharacterSet]];
OGRegularExpression  *regex =
    [OGRegularExpression regularExpressionWithString:@"\s+"];
return [regex replaceAllMatchesInString:theStringTrimmed withString:@" "]);

สั้นและหวาน

หากคุณต้องการวิธีแก้ปัญหาที่เร็วที่สุดชุดคำแนะนำที่สร้างขึ้นอย่างรอบคอบโดยใช้NSScannerอาจจะได้ผลดีที่สุด แต่จำเป็นก็ต่อเมื่อคุณวางแผนที่จะประมวลผลบล็อกข้อความขนาดใหญ่ (หลายเมกะไบต์)


มีเหตุผลที่จะใช้ OgreKit แทน RegExKitLite หรือไม่? regexkit.sourceforge.net มันมีการเรียก replaceOccurrencesOfRegex ที่คล้ายกันมากและทำงานบนไลบรารี RegEX ที่มีอยู่ (ไม่แน่ใจว่า Ogre เป็นเอนจิ้น RegEX ทั้งหมดหรืออะไร)
Kendall Helmstetter Gelner

ฉันแน่ใจว่าทั้งสองอย่างจะใช้ได้ ฉันไม่ได้ใช้ regexkit แต่เป็นคำแนะนำที่ดีที่จะทำ ผู้คนควรเลือกโดยพิจารณาจากไลบรารีที่เป็นพื้นฐาน ได้แก่ pcre ที่เข้ากันได้กับ PERL (RegExKitLite) และ Oniguruma (OgreKit) ที่เข้ากันได้กับ Ruby
Matt Gallagher

-1

อ้างอิงจาก @Mathieu Godart เป็นคำตอบที่ดีที่สุด แต่มีบางบรรทัดขาดหายไปคำตอบทั้งหมดเพียงแค่ลดช่องว่างระหว่างคำ แต่ถ้ามีแท็บหรือมีแท็บอยู่ในช่องว่างเช่นนี้: "นี่คือข้อความ \ t และ \ t แท็บระหว่าง, "ในโค้ดสามบรรทัดเราจะ: สตริงที่เราต้องการลดช่องว่างสีขาว

NSString * str_aLine = @"    this is text \t , and\tTab between      , so on    ";
// replace tabs to space
str_aLine = [str_aLine stringByReplacingOccurrencesOfString:@"\t" withString:@" "];
// reduce spaces to one space
str_aLine = [str_aLine stringByReplacingOccurrencesOfString:@" +" withString:@" "
                                                    options:NSRegularExpressionSearch
                                                      range:NSMakeRange(0, str_aLine.length)];
// trim begin and end from white spaces
str_aLine = [str_aLine stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

ผลลัพธ์คือ

"this is text , and Tab between , so on"

โดยไม่ต้องเปลี่ยนแท็บ resul จะเป็น:

"this is text    , and  Tab between , so on"

-1

คุณยังสามารถใช้อาร์กิวเมนต์ while ไม่มีเวทมนตร์ RegEx อยู่ในนั้นดังนั้นอาจจะเข้าใจง่ายขึ้นและเปลี่ยนแปลงได้ในอนาคต:

while([yourNSStringObject replaceOccurrencesOfString:@"  "
                         withString:@" "
                         options:0
                         range:NSMakeRange(0, [yourNSStringObject length])] > 0);

1
ไม่ตอบคำถาม :) ไม่ลบช่องว่างนำหน้าและต่อท้าย
hfossli

-1

การทำตามสองนิพจน์ทั่วไปจะได้ผลขึ้นอยู่กับข้อกำหนด

  1. @ "+" สำหรับการจับคู่ช่องว่างและแท็บ
  2. @ "\\ s {2,}" เพื่อจับคู่ช่องว่างแท็บและตัวแบ่งบรรทัด

จากนั้นใช้วิธีการอินสแตนซ์ของ nsstring stringByReplacingOccurrencesOfString:withString:options:range:เพื่อแทนที่ด้วยช่องว่างสีขาวเดียว

เช่น

[string stringByReplacingOccurrencesOfString:regex withString:@" " options:NSRegularExpressionSearch range:NSMakeRange(0, [string length])];

หมายเหตุ: ฉันไม่ได้ใช้ไลบรารี 'RegexKitLite' สำหรับฟังก์ชันข้างต้นสำหรับ iOS 5.x ขึ้นไป


โซลูชันนี้ไม่ได้ลบช่องว่างนำหน้าและต่อท้ายตามที่ OP ขอ
hfossli

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