Свернуть последовательности пробелов в один символ и обрезать строку

Рассмотрим следующий пример:

"    Hello      this  is a   long       string!   "

Я хочу преобразовать это в:

"Hello this is a long string!"

person Community    schedule 16.04.2009    source источник


Ответы (13)


ОС Х 10.7+ и iOS 3.2+

Используйте собственное решение для регулярных выражений, предоставленное hfossli.

В противном случае

Либо используйте свою любимую библиотеку регулярных выражений, либо используйте следующее собственное решение Cocoa:

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:@" "];
person Georg Schölly    schedule 15.09.2009
comment
Мне было бы любопытно сравнить производительность этого с заменой регулярного выражения с обрезкой для удаления концов. С одной стороны, у вас есть регулярное выражение, с которым нужно иметь дело. С другой стороны, у вас есть предикат. Либо требуется внутренняя обработка соответствующих выражений. - person lilbyrdie; 23.06.2011
comment
@lilbyrdie: Думаю, это зависит от строки, сколько в ней пробелов. Мое решение довольно медленное, потому что оно создает новый объект для каждой подстроки и отправляет вызовы метода каждой из них. - person Georg Schölly; 23.06.2011
comment
Прекрасный ответ, за который проголосовали как таковой, но я оспариваю ваше определение легкости. С уважением, бывший парень Python теперь в ObjC-land ;-) - person JK Laiho; 31.05.2012
comment
@JKLaiho: Учитывая, что большинству других решений нужны внешние библиотеки, я бы сказал, что это легко. :) Я думаю, что в долгосрочной перспективе они собираются отказаться от obj-c и заменить его гораздо более современным языком. Взгляните на проект MacRuby! - person Georg Schölly; 02.06.2012
comment
Вы меня рассмешили словами «не используйте сложные решения, если есть простые». Итак, самый простой вариант — [toBeTrimmed stringByReplacingOccurrencesOfString:@ withString:@] нет? Я все еще поддерживаю ваш ответ, но он определенно самый простой - person Mário Carvalho; 09.06.2013
comment
@MárioCarvalho Вопрос заключается в том, как удалить лишние пробелы, а не все. - person swilliams; 01.07.2013
comment
Я действительно не понимаю, почему разделение на массив и использование предиката хуже, чем регулярное выражение. Это не более читаемый код. Мне интересно узнать о производительности как в регулярном выражении, так и в массивном компоненте. - person hfossli; 07.08.2013
comment
@hfossli: Тот факт, что вам нужно добавить библиотеку и, возможно, обновлять ее, является сложной частью. Лично я бы, вероятно, использовал регулярное выражение, если бы оно было доступно, потому что оно гораздо лучше выражает ваши намерения. - person Georg Schölly; 19.08.2013
comment
@GeorgSchölly Regex доступен в NSFoundstion начиная с iOS 4. Вопрос не в решениях для iOS 3.2. Нет необходимости добавлять библиотеку. - person hfossli; 19.08.2013
comment
@GeorgSchölly Я добавил результаты производительности в свой ответ. Regex использует половину времени. - person hfossli; 18.10.2013
comment
@hfossli: я не удивлен вашими выводами, мой метод использует много дополнительной памяти. Не могли бы вы включить свое решение в мой ответ? Я изменю его на вики сообщества, если вы согласны. - person Georg Schölly; 18.10.2013

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!"

Возможные альтернативные шаблоны регулярных выражений:

  • Заменить только пробел: [ ]+
  • Заменить пробел и табуляцию: [ \\t]+
  • Заменить пробел, табуляцию и новую строку: \\s+

Обзор производительности

Простота расширения, производительность, количество строк кода и количество создаваемых объектов делают это решение подходящим.

person hfossli    schedule 06.08.2013
comment
Хфосли — самый элегантный ответ в моей книге. Кроме того, я только что узнал, что вы можете использовать регулярные выражения в stringByReplacingOccurrencesOfString:. Не могу поверить, что я этого не знал. - person davidf2281; 14.08.2013
comment
Потрясающий. Работал как шарм - person Kushal Ashok; 27.05.2015

На самом деле, есть очень простое решение:

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

(Источник)

person arikfr    schedule 15.09.2009
comment
Я думаю, что это устранит только начальные и конечные пробелы и устранит их все. это не будет иметь дело с hello foo - person Brian Postow; 15.09.2009
comment
d*mn окончаний строк и автоформат... он не работает с hello______foo (предположим, что _ -›, потому что форматирование комментариев затруднено) - person Brian Postow; 15.09.2009
comment
Почему вы, люди, голосуете за ответы, которые не дают решения вопроса? stringByTrimmingCharactersInSet не анализирует сторону строки, а только края. Ответ Георга Шолли - идеальный. - person Lukasz; 14.11.2011
comment
Это был не совсем ответ на вопрос, но он мне точно помог. Спасибо - person daveMac; 12.12.2011
comment
Отличный код для одновременного удаления начального и конечного пробелов. - person user523234; 12.02.2012
comment
Немного помогает, но не является подходящим ответом на заданный здесь вопрос. - person पवन; 05.12.2012
comment
Я не знаю, почему люди проголосовали за этот ответ. Что не совсем правильно. - person iOS_Developer; 11.06.2015

С регулярным выражением, но без необходимости какой-либо внешней структуры:

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

theString = [theString stringByReplacingOccurrencesOfString:@" +" withString:@" "
                       options:NSRegularExpressionSearch
                       range:NSMakeRange(0, theString.length)];
person MonsieurDart    schedule 04.11.2010
comment
Вам также все равно придется обрезать результат, иначе вы будете заполнены пробелами. Хотя это, наверное, самый простой ответ. - person lilbyrdie; 23.06.2011
comment
в документации для NSRegularExpressionSearch сказано, что он работает только с методами rangeOfString:... - person user102008; 05.07.2011

Однострочное решение:

NSString *whitespaceString = @" String with whitespaces ";

NSString *trimmedString = [whitespaceString
        stringByReplacingOccurrencesOfString:@" " withString:@""];
person TwoBeerGuy    schedule 03.08.2010
comment
Выручил меня :). Спасибо за это! - person thedom; 20.12.2010
comment
Хотя это полезно, оно удаляет все пробелы. OP в основном хочет уплотнения пробелов, например. обрезка с последующим сокращением последовательных пробелов до одного пробела. - person lilbyrdie; 23.06.2011
comment
Еще одно замечание: это решение не касается табуляции, новой строки или пробельных символов, кроме пробелов. - person cthulhu; 08.02.2012
comment
Это не отвечает на OP, а вместо этого удаляет все пробелы в строке, поэтому вы получаете @Stringwithwhitespaces - person charles; 17.09.2012

Это должно сделать это...

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:@" "];
person Barry Wark    schedule 16.04.2009
comment
Это действительно работает со строкой «а»? Насколько я вижу, это длина 1, это решение отфильтрует все разделенные слова размером 0 и 1. - person cthulhu; 08.02.2012
comment
Да, именно этого ответа я и ожидал. Спасибо +1 - person पवन; 05.12.2012

Другим вариантом регулярного выражения является RegexKitLite, который очень легко встроить в проект iPhone:

[theString stringByReplacingOccurencesOfRegex:@" +" withString:@" "];
person Daniel Dickison    schedule 17.04.2009

Попробуй это

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

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

Вот фрагмент из расширения 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;
}
person dmercredi    schedule 16.04.2009

Альтернативное решение: купите себе копию OgreKit (библиотека регулярных выражений Cocoa).

  • OgreKit (японская веб-страница — код на английском языке)
  • OgreKit (автоперевод Google):

Тогда вся функция:

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

Коротко и мило.

Если вам нужно самое быстрое решение, возможно, лучше всего подойдет тщательно составленная серия инструкций с использованием NSScanner, но это необходимо только в том случае, если вы планируете обрабатывать огромные (много мегабайт) блоки текста.

person Matt Gallagher    schedule 16.04.2009
comment
Есть ли причина использовать OgreKit вместо RegExKitLite? regexkit.sourceforge.net Он имеет очень похожий вызов replaceOccurrencesOfRegex и работает поверх существующих библиотек RegEX (не уверен, что Ogre - это целый движок RegEX или что-то в этом роде) - person Kendall Helmstetter Gelner; 18.04.2009
comment
Я уверен, что оба будут работать. Я не использовал regexkit, но это хорошее предложение. Люди должны выбирать на основе базовых библиотек: PERL-совместимый pcre (RegExKitLite) и Ruby-совместимый Oniguruma (OgreKit). - person Matt Gallagher; 20.04.2009

согласно @Mathieu Godart, лучший ответ, но какая-то строка отсутствует, все ответы просто сокращают пространство между словами, но когда есть вкладки или есть вкладка на месте пробела, например: «это текст \t и\tTab между, так далее "в трехстрочном коде мы будем: строка, которую мы хотим уменьшить пробелы

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"

без замены вкладки результат будет:

"this is text    , and  Tab between , so on"
person Kosar    schedule 02.02.2012

Вы также можете использовать простой аргумент while. Там нет магии RegEx, поэтому, возможно, его будет легче понять и изменить в будущем:

while([yourNSStringObject replaceOccurrencesOfString:@"  "
                         withString:@" "
                         options:0
                         range:NSMakeRange(0, [yourNSStringObject length])] > 0);
person Sven-Steffen Arndt    schedule 17.11.2013
comment
Не отвечает на вопрос :) Не удаляет начальные и конечные пробелы. - person hfossli; 05.01.2014

Следующие два регулярных выражения будут работать в зависимости от требований

  1. @" +" для сопоставления пробелов и вкладок
  2. @"\\s{2,}" для сопоставления пробелов, табуляции и разрывов строк

Затем примените метод экземпляра nsstring stringByReplacingOccurrencesOfString:withString:options:range:, чтобы заменить их одним пробелом.

e.g.

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

Примечание. Я не использовал библиотеку «RegexKitLite» для вышеуказанной функциональности для iOS 5.x и выше.

person apalvai    schedule 19.07.2013
comment
Это решение не удаляет начальные и конечные пробелы, как того требует ОП. - person hfossli; 03.12.2014
comment
Начальные/конечные пробелы @hfossli можно удалить, напрямую вызвав метод NSString stringByTrimmingCharactersInSet: с набором символов новой/белой строки. Вышеупомянутое решение состояло в том, чтобы удалить избыточные пробелы независимо от их местоположения. - person apalvai; 13.12.2014