【问题标题】:Find and replace long words in an NSString? [closed]查找和替换 NSString 中的长单词? [关闭]
【发布时间】:2013-05-09 06:58:07
【问题描述】:

我正在尝试编写一个搜索 NSString 的方法,确定字符串中的单个单词是否超过 6 个字符,然后用其他单词替换该单词(例如“hello”)。

我从一个很长的段落开始,最后需要一个 NSString 对象,其格式和间距不受查找和替换的影响。

【问题讨论】:

  • 你能改写你的问题吗?很难理解你的问题到底是什么。任何尝试过的代码 sn-p 都会很棒。
  • 这个问题到底是怎么解决的?投票重新开放。
  • @Monolo 前两次投票发生在编辑之前(当时问题确实处于糟糕状态)。由于没有办法投票反对近距离投票,任何只有一次近距离投票的问题最终都会被关闭。人们只是在评论部分偶然发现这些帖子并关闭它们,而不用太关心在第一次近距离投票之前发生的事情。

标签: objective-c nsstring


【解决方案1】:

为什么是另一个答案?

使用componentsSeparatedByString: 的简单解决方案存在一些微妙的问题:

  1. 标点符号不作为单词分隔符处理。
  2. 除了空格字符(换行符、制表符)之外的空白字符被简单地删除。
  3. 长字符串会浪费大量内存。
  4. 很慢。

示例

假设“-”的替换词是一个类似...的字符串

“基本上”,D.H.C.总结,
“bokanovskification 包括一系列发展停滞。”

... 会导致 ...

– D.H.C. - 一系列的 - 的 -

...而正确的输出是:

“-”,D.H.C. –,
”– – 一系列 – of –。”

解决方案

幸运的是,Cocoa 中有一个更好但更简单的解决方案:-[NSString enumerateSubstringsInRange:options:usingBlock:]

它提供了对options 参数定义的子字符串的快速迭代。一种可能性是NSStringEnumerationByWords,它枚举所有实际上是真实单词的子字符串(在当前语言环境中)。它甚至可以检测不使用分隔符(空格)分隔单词的语言中的单个单词,例如日语。

比较解决方案

Here's a simple demo project 适用于行话文件(1.6 MB,237,239 字)。它比较了三种不同的解决方案:

  1. componentsSeparatedByString:270 毫秒
  2. enumerateSubstringsInRange:125 毫秒
  3. stringByReplacingOccurrencesOfString,如@Monolo 所述:200 毫秒

实施

它的核心是替换循环:

NSMutableString *result = [NSMutableString stringWithCapacity:[originalString length]];
__block NSUInteger location = 0;
[originalString enumerateSubstringsInRange:(NSRange){0, [originalString length]}
                                   options:NSStringEnumerationByWords | NSStringEnumerationLocalized | NSStringEnumerationSubstringNotRequired
                                usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {

                                    if (substringRange.length > maxChar) {
                                        NSString *charactersBetweenLongWords = [originalString substringWithRange:(NSRange){ location, substringRange.location - location }];
                                        [result appendString:charactersBetweenLongWords];
                                        [result appendString:replaceWord];
                                        location = substringRange.location + substringRange.length;
                                    }

                                }];
[result appendString:[originalString substringFromIndex:location]];

警告

正如Monolo 所指出的,建议的代码使用NSString 的长度来确定单词的字符数。至少可以说,这是一个值得怀疑的方法。事实上,字符串的length 指定了用于对字符串进行编码的代码片段数,该值通常与人类假设的字符数不同。

由于术语“字符”在不同的上下文中具有不同的含义,并且 OP 没有指定使用哪种字符数,所以我只是将代码保持原样。如果您想要不同的计数,请参阅讨论该主题的文档:

【讨论】:

  • 只是吹毛求疵,如果您考虑使用复合字符编码的单词“Ålborg”,例如:@“A\u030Alborg”,您的实现将触发替换。 substringRange 当然只考虑“原始”(因为需要更好的词)字符。我已经尝试使用计算复合字符的函数,它仍然有效,但我不知道计算复合字符的禁食方法是什么。 (哦,这里是银行假期,所以是时候进行学术研究了:-))
  • @Monolo 很好的发现,感谢您指出这个错误!组合字符序列、代理对和辅音簇是字符串处理代码中许多错误的常见来源。像往常一样,Apple 的“字符串编程指南、字符和字素簇”是一本极好的读物:developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/…
  • @Monolo 对你的一些随机答案进行了投票(在缺乏评论赞誉的情况下)。
  • 这是一个问题,alastairanswer 以方便的形式提供了一些有用的细节。
【解决方案2】:

正如您从答案中看到的那样,有几种方法可以完成您所追求的目标,但我个人更喜欢使用NSString 类的stringByReplacingOccurrencesOfString:withString:options:range: 方法,该方法正是为了用另一个字符串替换子字符串。

在您的情况下,我们需要使用 NSRegularExpressionSearch 选项,该选项将允许识别具有 7 个或更多字母(即,如您所说的超过 6 个字母)的单词。

如果您使用 \w* 字符表达式,您将自动获得 Unicode 支持,因此它适用于 Apple(实际上是 ICU)支持的多种语言。

是这样的:

NSString *stringWithLongWords = @"There are some words of extended length in this text. One of them is Escher's. They will be identified with a regular expression and changed for some arbitrary word.";

NSString *overSixCharsPattern = @"(?w)\\b[\\w]{7,}\\b";
NSString *replacementString   = @"hello";

NSString *result = [stringWithLongWords stringByReplacingOccurrencesOfString: overSixCharsPattern
                                                                  withString: replacementString
                                                                     options: NSRegularExpressionSearch
                                                                       range: NSMakeRange(0, stringWithLongWords.length)];

\b 表达式表示单词边界,确保匹配和替换整个单词。 w 修饰符使\b 使用更自然的单词边界定义。具体来说,它处理字符串“Escher's”,@NikolaiRuhe 提到的示例。文档here,具体讨论了边界检测here

还要注意,文字 NSString(即,您直接在 Objective-C 源文件中输入的)需要在源代码中使用两个反斜杠来在生成的字符串中生成一个。

NSString documentation有更多信息

* 从技术上讲,\w 匹配单词字符,其中还包括正则表达式使用的定义中的数字。

【讨论】:

  • 在正则表达式单词检测和enumerateSubstringsInRange: 使用的单词检测之间存在一些有趣的差异。正则表达式将“Escher's”视为两个词,而 Cocoa 文本系统将其视为一个词。类似“think.com”或“AIWORD.RF”等域名或文件名。
  • 性能介于componentsSeparatedByStringenumerateSubstringsInRange 变体之间。
  • @NikolaiRuhe 虽然我故意不考虑性能(过早的优化和所有这些),但我可以看到人们希望它如何检测英语中的属格和收缩(“it's”)。在我的辩护中,我敢说它在挪威文本上的效果令人满意:-) 我想我应该开始考虑enumerateSubstrings... 和朋友们。 OTOH,我是否希望它将互联网域名识别为一个词是一个哲学问题,至少需要一杯好酒来分析。
  • 我同意。我刚刚发现有趣的事实是 enumerateSubstrings 似乎使用了更复杂的语言模型,因此以不同的方式识别分隔符。我发现 Apple 的字符串操作函数通常使用简单,但在表面之下非常先进。
猜你喜欢
  • 2014-07-26
  • 2016-08-14
  • 1970-01-01
  • 2012-01-04
  • 2011-04-25
  • 1970-01-01
  • 2012-10-27
  • 1970-01-01
  • 2021-12-26
相关资源
最近更新 更多