代码之家  ›  专栏  ›  技术社区  ›  d11wtq Vadim Baryshev

从nsstring提取已知模式子字符串(不带regex)

  •  0
  • d11wtq Vadim Baryshev  · 技术社区  · 15 年前

    我真的很想把regexkit(或我自己的libpcre包装器)放到我的项目中,这样做,但在我这样做之前,我想知道cocoa开发人员如何在没有真正复杂的代码或没有与regexkit或另一个正则表达式库链接的情况下完成这一半基本工作。

    我觉得可可不包括 任何 正则表达式匹配功能。我已经习惯了用正则表达式来处理各种事情,没有它们我就迷失了方向。如果没有它们,我可以做我需要的事情,但是代码相当复杂。所以,可可开发人员,我问你,“可可方法”是什么?

    就我而言,这个问题是编程中的一个日常问题。Cocoa必须通过内置功能来实现这一点。请注意,我想要匹配更改的元素的位置,有时会出现“引号”。空白是可变的。

    使用以下字符串:

    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
    

    从所有这些字符串中,您将如何使用内置的cocoa类来确定mime类型(例如,text/plain)和字符集(例如,utf-8)?

    最后我会表演一系列 -rangeOfString: 以及子字符串调用,以及处理可选引号的条件检查等。是否有一种方法可以使用nsscanner进行此操作?nsscanner类对我来说似乎有一个相当幼稚的API。

    有点像C sscanf() 对于nsstring对象来说,这是一个理想的匹配。我的大多数字符串解析需求都很简单,比如这个例子,所以当我习惯了正则表达式的时候,它们可能是多余的?

    edit代码有点冗长,但事实证明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循环读取“;然后检查它是否是我正在扫描的属性,可以使这变得更聪明。

    我敢说它比使用regex更快地进行基准测试,而且我相当长的代码可以重构成更小的代码。

    2 回复  |  直到 15 年前
        1
  •  1
  •   JeremyP    15 年前

    如果这些是HTTP内容类型的头,从技术上讲,根据我对rfc2616的理解,第二个头是非法的。不能引用字符集名称。尽管如此,你不能控制你的输入,如果你得到了它们,你需要处理它们。

    不管怎样,假设我们 谈到HTTP报头,即使我手头有一个regex库,我还是想写一个合适的解析器。假设您希望有点懒惰,没有regex库或解析器,您需要这样做:

    • 条带“内容长度:”。
    • 使用-componentsseparatedbystring:以分号分隔。

    mime类型是前导空格和尾随空格的第一部分。

    现在是棘手的部分。遍历其余的每个组件。

    • 对于您所在的部分,请确保拆分的分号未嵌入到字符串中。最简单的方法是计算 未逃逸的 双引号字符,并确保零或两个。如果yuou在带引号的分号上进行了拆分,请重新连接下一个组件,然后重复
    • 在=符号处拆分
    • 如果第一部分是 charset (不区分大小写)你找到了你要找的那个。第二部分是实际的字符集-去掉空白并包含双引号。

    上面的内容非常复杂,可能有一些边缘案例失败了,但是您创建的任何执行相同操作的正则表达式也会很复杂,有边缘案例失败,不可读,并且无法使用Xcode调试器进行调试。

        2
  •  2
  •   Cory Kilger    15 年前

    我认为你应该按照你最初的直觉去做。使用 RegexKitLite . 添加到项目中非常小和简单。

    另一个选择是,如果这是针对使用iPhone OS 3.2的iPhone或iPad,您可以使用新的 NSRegularExpressionSearch 选项与 -rangeOfCharacterFromSet:options: .

    但是,如果不使用正则表达式,我将有一系列indexof、rangeof和substring调用。它可能只有六行,但仍然不像正则表达式那么简单和漂亮。

    推荐文章