Zwiń sekwencje białych znaków w jeden znak i przytnij ciąg

122

Rozważmy następujący przykład:

"    Hello      this  is a   long       string!   "

Chcę przekonwertować to na:

"Hello this is a long string!"
hfossli
źródło

Odpowiedzi:

125

OS X 10.7+ i iOS 3.2+

Użyj natywnego rozwiązania regexp dostarczonego przez hfossli.

Inaczej

Użyj swojej ulubionej biblioteki wyrażeń regularnych lub użyj następującego rozwiązania używanego w kakao:

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:@" "];
Georg Schölly
źródło
4
Byłbym ciekawy porównania wydajności tego do zamiany wyrażenia regularnego z przycięciem, aby usunąć końce. Z jednej strony masz do czynienia z wyrażeniem regularnym. Z drugiej strony masz predykat. Każda z nich wymaga wewnętrznego przetwarzania odpowiednich wyrażeń.
lilbyrdie
@lilbyrdie: Myślę, że to zależy od ciągu, ile jest białych znaków. Moje rozwiązanie jest dość powolne, ponieważ tworzy nowy obiekt dla każdego podciągu i wysyła wywołania metod do każdego z nich.
Georg Schölly
2
Dobra odpowiedź, jako taka pozytywnie oceniona, ale kwestionuję twoją definicję „łatwego”. Z poważaniem, były facet Pythona teraz w ObjC-land ;-)
JK Laiho
2
Rozśmieszyłeś mnie słowami „nie używaj skomplikowanych rozwiązań, jeśli jest proste”. Więc najłatwiejszym jest [toBeTrimmed stringByReplacingOccurrencesOfString: @ "" withString: @ ""] no? Nadal głosuję za twoją odpowiedzią, ale jest zdecydowanie najłatwiejsza
Mário Carvalho
2
@ MárioCarvalho Pytanie dotyczy tego, jak usunąć nadmiar białych znaków, a nie wszystkie.
swilliams
52

Regex i NSCharacterSet są tutaj, aby Ci pomóc. To rozwiązanie usuwa początkowe i końcowe białe znaki, a także wiele białych znaków.

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]];

Logowanie finaldaje

"Hello this is a long string!"

Możliwe alternatywne wzorce regex:

  • Zastąp tylko spację: [ ]+
  • Zastąp spację i tabulatory: [ \\t]+
  • Zastąp spację, tabulatory i znaki nowej linii: \\s+

Podsumowanie wydajności

Łatwość rozbudowy, wydajność, liczba wierszy kodu i liczba tworzonych obiektów sprawia, że ​​jest to odpowiednie rozwiązanie.

hfossli
źródło
3
hfossli to najbardziej elegancka odpowiedź w mojej książce. Poza tym właśnie dowiedziałem się, że możesz używać wyrażeń regularnych w stringByReplacingOccurrencesOfString:. Nie mogę uwierzyć, że tego nie wiedziałem.
davidf2281
1
Niesamowite. Działał jak urok
Kushal Ashok
41

Właściwie jest na to bardzo proste rozwiązanie:

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

( Źródło )

arikfr
źródło
29
Myślę, że to wyeliminuje tylko spacje początkowe i końcowe oraz wyeliminuje je wszystkie. nie zajmie się „witaj foo”
Brian Postow
2
d * mn końcówki wierszy i automatyczne formatowanie ... nie dotyczy "hello______foo" (załóżmy, że _ -> "", ponieważ formatowanie komentarzy jest trudne)
Brian Postow
32
Dlaczego głosujecie na i odpowiedzi, które nie dają odpowiedzi na pytanie? stringByTrimmingCharactersInSet nie analizuje iside łańcucha, ale tylko krawędzie. Odpowiedź Georga Sholly'ego jest idealna.
Łukasz
3
Nie była to dokładna odpowiedź na pytanie, ale z pewnością pomogła mi. Dzięki
daveMac,
1
Doskonały kod do jednoczesnego usuwania spacji wiodącej i końcowej.
user523234
13

Z wyrażeniem regularnym, ale bez potrzeby korzystania z żadnej zewnętrznej struktury:

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

theString = [theString stringByReplacingOccurrencesOfString:@" +" withString:@" "
                       options:NSRegularExpressionSearch
                       range:NSMakeRange(0, theString.length)];
MonsieurDart
źródło
Wtedy nadal będziesz musiał przyciąć wynik lub zostaniesz wypełniony spacjami. To chyba najprostsza odpowiedź.
lilbyrdie
2
dokumentacja dla NSRegularExpressionSearchmówi, że działa tylko z rangeOfString:...metodami
user102008
9

Jednoliniowe rozwiązanie:

NSString *whitespaceString = @" String with whitespaces ";

NSString *trimmedString = [whitespaceString
        stringByReplacingOccurrencesOfString:@" " withString:@""];
TwoBeerGuy
źródło
2
Pomogło mi :). Dziękuję za to!
dom
5
Chociaż jest to przydatne, usuwa wszystkie spacje. OP w zasadzie chce kompaktowania białych znaków, np. Przycinania, po którym następuje redukcja kolejnych białych znaków do pojedynczej białej spacji.
lilbyrdie
Kolejna uwaga, to rozwiązanie nie zajmuje się tabulatorami, znakami nowej linii ani białymi znakami innymi niż spacje.
fwielstra
2
To nie odpowiada na OP, ale zamiast tego usuwa wszystkie spacje w ciągu, więc kończy się na @ "Stringwithwhitespaces"
charles
6

To powinno wystarczyć ...

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:@" "];
Barry Wark
źródło
1
Czy to faktycznie działa z ciągiem „a”? Ma długość 1, o ile widzę, to rozwiązanie odfiltruje wszystkie podzielone słowa o rozmiarze 0 i 1.
fwielstra
Tak, to odpowiedź, której się spodziewałem. Dzięki +1
पवन
4

Inną opcją dla wyrażenia regularnego jest RegexKitLite , który można bardzo łatwo osadzić w projekcie iPhone'a:

[theString stringByReplacingOccurencesOfRegex:@" +" withString:@" "];
Daniel Dickison
źródło
3

Spróbuj tego

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

while ([theString rangeOfString:@"  "].location != NSNotFound) {
    theString = [theString stringByReplacingOccurrencesOfString:@"  " withString:@" "];
}
sinh99
źródło
3

Oto fragment NSStringrozszerzenia, gdzie "self"jest NSStringinstancja. Można go użyć do zwinięcia ciągłych białych znaków w jedną spację, przekazując [NSCharacterSet whitespaceAndNewlineCharacterSet]i ' 'do dwóch argumentów.

- (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;
}
dmercredi
źródło
-1

Alternatywne rozwiązanie: zdobądź kopię OgreKit (biblioteka wyrażeń regularnych Cocoa).

  • OgreKit (japońska strona internetowa - kod jest w języku angielskim)
  • OgreKit (automatyczne tłumaczenie Google):

Cała funkcja jest zatem:

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

Krótkie i słodkie.

Jeśli szukasz najszybszego rozwiązania, starannie skonstruowana seria instrukcji z użyciem NSScannerprawdopodobnie zadziałaby najlepiej, ale byłoby to konieczne tylko wtedy, gdy planujesz przetwarzać ogromne (wiele megabajtów) bloki tekstu.

Matt Gallagher
źródło
Czy istnieje powód, aby używać OgreKit zamiast RegExKitLite? regexkit.sourceforge.net Ma bardzo podobne wywołanie replaceOccurrencesOfRegex i działa na istniejących bibliotekach RegEX (nie jestem pewien, czy Ogre to cały silnik RegEX czy co)
Kendall Helmstetter Gelner
Jestem pewien, że oba będą działać. Nie korzystałem z regexkit, ale jest to dobra sugestia. Ludzie powinni wybierać na podstawie bazowych bibliotek: pcre kompatybilny z PERL (RegExKitLite) i kompatybilny z Ruby Oniguruma (OgreKit).
Matt Gallagher
-1

według @Mathieu Godart jest najlepszą odpowiedzią, ale brakuje jakiejś linii, wszystkie odpowiedzi po prostu zmniejszają odstępy między słowami, ale jeśli mają tabulatory lub mają spację w miejscu, na przykład: "to jest tekst \ t i \ tTab między, tak dalej ”w trzywierszowym kodzie będziemy: ciąg, który chcemy zredukować

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]];

wynik to

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

bez wymiany zakładki wynik będzie:

"this is text    , and  Tab between , so on"
Kosar
źródło
-1

Możesz także użyć prostego argumentu while. Nie ma tam magii RegEx, więc może łatwiej będzie to zrozumieć i zmienić w przyszłości:

while([yourNSStringObject replaceOccurrencesOfString:@"  "
                         withString:@" "
                         options:0
                         range:NSMakeRange(0, [yourNSStringObject length])] > 0);
Sven-Steffen Arndt
źródło
1
Nie odpowiada na pytanie :) Nie usuwa początkowych i końcowych białych znaków.
hfossli
-1

Następujące dwa wyrażenia regularne działałyby w zależności od wymagań

  1. @ „+” dla pasujących spacji i tabulatorów
  2. @ "\\ s {2,}" do dopasowywania spacji, tabulatorów i podziałów linii

Następnie zastosuj metodę instancji nsstring, stringByReplacingOccurrencesOfString:withString:options:range:aby zastąpić je pojedynczym odstępem.

na przykład

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

Uwaga: nie korzystałem z biblioteki „RegexKitLite” dla powyższej funkcjonalności dla iOS 5.x i nowszych.

apalvai
źródło
To rozwiązanie nie usuwa wiodących i końcowych białych znaków, o co prosi OP.
hfossli
@hfossli wiodące / końcowe spacje można usunąć, bezpośrednio wywołując metodę stringByTrimmingCharactersInSet: z NSString z zestawem znaków nowej / białej linii. Powyższe rozwiązanie polegało na usunięciu zbędnych przestrzeni niezależnie od ich lokalizacji.
apalvai