Извлечь подстроку известного шаблона из NSString (без регулярного выражения) - PullRequest
0 голосов
/ 26 мая 2010

Мне очень хочется добавить RegexKit (или мою собственную оболочку libpcre) в мой проект, чтобы сделать это, но прежде чем я это сделаю, я хочу узнать, как разработчикам Cocoa удается выполнить половину этих базовых вещей без действительно сложного код или без связи с RegexKit или другой библиотекой регулярных выражений.

Мне кажется, что Какао не включает никаких функций сопоставления регулярных выражений. Я так привык использовать регулярные выражения для всех видов вещей, что я потерял без них. Я могу делать то, что мне нужно без них, но код будет довольно запутанным. Итак, разработчики Какао, я спрашиваю вас, что такое «способ Какао», чтобы сделать это ...

Эта проблема - повседневная проблема в программировании, насколько я понимаю. Какао должен иметь способы сделать это с помощью встроенных функций. Обратите внимание, что положение элементов, которые я хочу сопоставить, изменяется, и иногда присутствуют «кавычки». Пробелы являются переменными.

Взять следующие строки:

Content-Type: application/xml; charset=utf-8

Content-Type: text/html; charset="iso-8859-1"

Content-Type: text/plain;
 charset=us-ascii

Content-Type: text/plain; name="example.txt"; charset=utf-8

Из всех этих строк, как бы вы определили тип MIME (например, text / plain) и кодировку (например, utf-8), используя только встроенные классы Какао?

Я бы закончил тем, что выполнил серию вызовов -rangeOfString: и подстрок, с условными проверками для обработки необязательных кавычек и т. Д. Есть ли способ сделать это с NSScanner? Класс NSScanner, кажется, имеет довольно наивный API для меня.

Что-то вроде C's sscanf(), которое работает для объектов NSString, было бы идеальным выбором. Большинство моих потребностей в разборе строк просты, такие как этот пример, так что, может быть, регулярные выражения, хотя я к ним привык, излишни?

РЕДАКТИРОВАТЬ | Код немного затянут, но получается, что с NSScanner довольно легко работать. Он в основном идет по твоей струне, делая то, что ты говоришь. Самая раздражающая часть создания NSCharacterSet необходимых экземпляров.

- (void)testNSScannerUseCase {
  NSString *testString = @"Content-type: application/xml; name=\"test\";\n charset=\"utf-8\"";

  unsigned int a = 'a', zero = '0';

  // There's probably a quicker way than to make these character sets this way
  NSMutableCharacterSet *alphaNumSet = [NSMutableCharacterSet characterSetWithRange:NSMakeRange(a, 26)];
  [alphaNumSet addCharactersInRange:NSMakeRange(zero, 10)];

  NSMutableCharacterSet *mimeTypeSet = [NSMutableCharacterSet characterSetWithCharactersInString:@"/-"];
  [mimeTypeSet formUnionWithCharacterSet:alphaNumSet];

  NSMutableCharacterSet *charsetSet = [NSMutableCharacterSet characterSetWithCharactersInString:@"-"];
  [charsetSet formUnionWithCharacterSet:alphaNumSet];

  // Initialize a case-insensitive scanner
  NSScanner *scanner = [NSScanner scannerWithString:testString];
  [scanner setCaseSensitive:NO];

  // Prepare to capture mime-type
  NSString *mimeType = nil;

  // Skip past the Content-Type: section
  if ([scanner scanUpToString:@":" intoString:NULL] && [scanner scanString:@":" intoString:NULL]) {
    [scanner scanCharactersFromSet:mimeTypeSet intoString:&mimeType];
  }

  GHAssertEqualStrings(@"application/xml", mimeType, @"Mime-type should be application/xml");

  // Prepare to look for the charset attribute
  NSString *charset = nil;

  // Ignore quotes as well as whitespace
  [scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@"\r\n\t \""]];

  // Skip past the charset attribute declaration
  if ([scanner scanUpToString:@"charset=" intoString:NULL]
    && [scanner scanString:@"charset=" intoString:NULL]) {

    [scanner scanCharactersFromSet:charsetSet intoString:&charset];
  }

  GHAssertEqualStrings(@"utf-8", charset, @"Charset should be utf-8");
}

Это можно сделать немного умнее, используя цикл while, читающий до ";" затем проверим, проверяет ли это атрибут.

Осмелюсь сказать, что это тесты быстрее, чем использование регулярных выражений, и что мой довольно длинный код может быть реорганизован до чего-то гораздо меньшего.

Ответы [ 2 ]

2 голосов
/ 26 мая 2010

Я думаю, что вы должны идти со своим первоначальным инстинктом. Используйте RegexKitLite . Это очень маленький и простой для добавления в проект.

Другой вариант, если это для iPhone или iPad с iPhone OS 3.2, вы можете использовать новый параметр NSRegularExpressionSearch с -rangeOfCharacterFromSet:options:.

Однако, если бы я не собирался использовать регулярные выражения, у меня была бы серия вызовов indexOf, rangeOf и substring. Вероятно, это будет всего полдюжины строк, но все же не так просто и красиво, как регулярные выражения.

1 голос
/ 26 мая 2010

Если это заголовки HTTP Content-Type, технически второй недопустим согласно моему прочтению RFC2616. Вы не цитируете имена наборов символов. Сказав это, вы не можете контролировать свой вклад, и если вы получаете их, вам нужно иметь дело с ними.

В любом случае, если мы говорим о HTTP-заголовках, у меня возникнет соблазн написать правильный синтаксический анализатор, даже если бы у меня была библиотека регулярных выражений. Предполагая, что вы хотите быть немного ленивым, без библиотеки регулярных выражений или анализатора, вам нужно сделать что-то вроде этого:

  • Полоска "Content-Length:".
  • Используйте -componentsSeparatedByString: для разделения на точки с запятой.

MIME-тип - это первая часть, отделенная от начальных и конечных пробелов.

Теперь самое сложное. Итерация по каждому из оставшихся компонентов.

  • для части, в которой вы находитесь, убедитесь, что точка с запятой, на которую вы разбили, не была встроена в строку. Самый простой способ сделать это - подсчитать количество неэкранированных символов двойной кавычки и убедиться, что они равны нулю или двум. Если вы разбили точку с запятой в кавычках, снова включите следующий компонент и повторите
  • разделить на знак =
  • если первая часть charset (без учета регистра), вы нашли найденную искомую. Вторая часть - это действительный набор символов - уберите пробелы и заключите в двойные кавычки.

Вышеприведенное довольно сложное, и, возможно, есть крайние случаи, в которых оно не выполняется, но тогда любое регулярное выражение, которое вы создаете для этого, также будет сложным, будет иметь ошибки крайнего случая, быть нечитаемым и невозможно отладить с помощью отладчика Xcode.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...