string NSString 中某个字符出现的次数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/938095/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Number of Occurrences of a Character in NSString
提问by Elliot
I have an NSString
or NSMutableString
and would like to get the number of occurrences of a particular character.
我有一个NSString
orNSMutableString
并且想获取特定字符的出现次数。
I need to do this for quite a few characters -- uppercase English characters in this case -- so it would be nice for it to be quick.
我需要为相当多的字符执行此操作 - 在这种情况下为大写英文字符 - 所以它会很快。
采纳答案by CynicismRising
replaceOccurrencesOfString:withString:options:range:
will return the number of characters replaced in a NSMutableString
.
replaceOccurrencesOfString:withString:options:range:
将返回 a 中替换的字符数NSMutableString
。
[string replaceOccurrencesOfString:@"A"
withString:@"B"
options:NSLiteralSearch
range:NSMakeRange(0, [receiver length])];
回答by gbaor
You can do this in one line. For example, this counts the number of spaces:
您可以在一行中完成此操作。例如,这会计算空格数:
NSUInteger numberOfOccurrences = [[yourString componentsSeparatedByString:@" "] count] - 1;
回答by Jacque
Try this category on NSString:
在 NSString 上试试这个类别:
@implementation NSString (OccurrenceCount)
- (NSUInteger)occurrenceCountOfCharacter:(UniChar)character
{
CFStringRef selfAsCFStr = (__bridge CFStringRef)self;
CFStringInlineBuffer inlineBuffer;
CFIndex length = CFStringGetLength(selfAsCFStr);
CFStringInitInlineBuffer(selfAsCFStr, &inlineBuffer, CFRangeMake(0, length));
NSUInteger counter = 0;
for (CFIndex i = 0; i < length; i++) {
UniChar c = CFStringGetCharacterFromInlineBuffer(&inlineBuffer, i);
if (c == character) counter += 1;
}
return counter;
}
@end
This one is approximately 5 times faster than the componentsSeparatedByString:
approach.
这种componentsSeparatedByString:
方法比方法快大约 5 倍。
回答by Abizern
Whenever you are looking for things in a NSString
, try using NSScanner
first.
每当您在 a 中查找内容时NSString
,请先尝试使用NSScanner
。
NSString *yourString = @"ABCCDEDRFFED"; // For example
NSScanner *scanner = [NSScanner scannerWithString:yourString];
NSCharacterSet *charactersToCount = [NSCharacterSet characterSetWithCharactersInString:@"C"]; // For example
NSString *charactersFromString;
if (!([scanner scanCharactersFromSet:charactersToCount
intoString:&charactersFromString])) {
// No characters found
NSLog(@"No characters found");
}
// should return 2 for this
NSInteger characterCount = [charactersFromString length];
回答by vikingosegundo
Nowadays the first thing that come to my mind for something like that: NSCountedSet
现在我想到的第一件事就是:NSCountedSet
NSString *string = @"AAATTC";
NSMutableArray *array = [NSMutableArray array];
[string enumerateSubstringsInRange:NSMakeRange(0, [string length]) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
[array addObject:substring];
}] ;
NSCountedSet * set = [[NSCountedSet alloc] initWithArray:array];
for (NSString *nucleobase in @[@"C", @"G", @"A", @"T"]){
NSUInteger count = [set countForObject:nucleobase];
NSLog(@"%@: %lu", nucleobase, (unsigned long)count);
}
logs:
日志:
C: 1
G: 0
A: 3
T: 2
回答by pierrot3887
Your solution did not work for me, I added a condition in the loop to increment numberOfChar only if mainScanner has reached the end of the string :
您的解决方案对我不起作用,我在循环中添加了一个条件,仅当 mainScanner 到达字符串末尾时才增加 numberOfChar :
NSString *yourString = @"ABCCDEDRFFED"; // For example
NSScanner *mainScanner = [NSScanner scannerWithString:yourString];
NSString *temp;
NSInteger numberOfChar=0;
while(![mainScanner isAtEnd])
{
[mainScanner scanUpToString:@"C" intoString:&temp];
if(![mainScanner isAtEnd]) {
numberOfChar++;
[mainScanner scanString:@"C" intoString:nil];
}
}
Note that this is a quick fix, I don't have time to make an elegant solution...
请注意,这是一个快速解决方案,我没有时间制定优雅的解决方案......
回答by C?ur
Performance comparison for the different Objective-C solutions.
不同 Objective-C 解决方案的性能比较。
Assume that all the methods below are NSString extensions (inside @implementation NSString (CountOfOccurrences)
).
假设下面的所有方法都是 NSString 扩展(内部@implementation NSString (CountOfOccurrences)
)。
As a sample, I've used a random generated string of length 100000000 using all Latin characters (CharacterSet(charactersIn: "\u{0020}"..."\u{036F}")
in Swift). And the character to count was @"a"
.
作为示例,我使用所有拉丁字符(CharacterSet(charactersIn: "\u{0020}"..."\u{036F}")
在 Swift 中)使用随机生成的长度为 100000000 的字符串。要计数的字符是@"a"
.
Tests performed on Xcode 10.3 on Simulator in release configuration.
在 Xcode 10.3 上在发布配置中在模拟器上执行的测试。
Fast solutions (exact character-by-character equivalence)
快速解决方案(精确的逐字符等效)
There are two ways to count for a character: using NSLiteralSearch
or not. The count will be different and the performance will be fundamentally affected. For fastest results, we will perform exact character-by-character equivalence. Below four solutions give very close performance results.
有两种计算字符的方法:使用NSLiteralSearch
或不使用。计数将不同,性能将受到根本影响。为了最快的结果,我们将执行精确的逐字符等效。以下四种解决方案给出了非常接近的性能结果。
1. Fastest solution: an adaptation of CynicismRising answer.
1. 最快的解决方案:改编自 CynicismRising 的答案。
Using replaceOccurrencesOfString:withString:options:range:
. This is the fastest solution in all scenarios: even if you replace NSLiteralSearch
with kNilOptions
, you're still faster than pierrot3887 scanner solution.
使用replaceOccurrencesOfString:withString:options:range:
. 这是所有场景中最快的解决方案:即使您替换NSLiteralSearch
为kNilOptions
,您仍然比 pierrot3887 扫描仪解决方案更快。
- (NSUInteger)countOccurrencesOfString:(NSString *)stringToFind
{
return [[NSMutableString stringWithString:self] replaceOccurrencesOfString:stringToFind
withString:stringToFind
options:NSLiteralSearch
range:NSMakeRange(0, self.length)];
}
2. Second fastest, another adaptation of CynicismRising answer.
2.第二快,另一个改编的 CynicismRising 答案。
Using stringByReplacingOccurrencesOfString:withString:options:range:
.
使用stringByReplacingOccurrencesOfString:withString:options:range:
.
- (NSUInteger)countOccurrencesOfString:(NSString *)stringToFind
{
NSString *strippedString = [self stringByReplacingOccurrencesOfString:stringToFind
withString:@""
options:NSLiteralSearch
range:NSMakeRange(0, self.length)];
return (self.length - strippedString.length) / stringToFind.length;
}
3. Third fastest, Jacque solution.
3. 第三快,Jacque 解决方案。
Using CFStringGetCharacterFromInlineBuffer
.
See https://stackoverflow.com/a/15947190/1033581.
使用CFStringGetCharacterFromInlineBuffer
. 请参阅https://stackoverflow.com/a/15947190/1033581。
4. Fourth fastest, a conversion of my Swift answerto Objective-C.
4.第四快,将我的 Swift 答案转换为 Objective-C。
Using rangeOfString:options:range:
.
使用rangeOfString:options:range:
.
- (NSUInteger)countOccurrencesOfString:(NSString *)stringToFind
{
//assert(stringToFind.length);
NSUInteger count = 0;
NSRange searchRange = NSMakeRange(0, self.length);
NSRange foundRange;
while ((void)(foundRange = [self rangeOfString:stringToFind options:NSLiteralSearch range:searchRange]), foundRange.length) {
count += 1;
NSUInteger loc = NSMaxRange(foundRange);
searchRange = NSMakeRange(loc, self.length - loc);
}
return count;
}
Slow solutions
缓慢的解决方案
The below solutions do not use NSLiteralSearch
and do not perform exact character-by-character equivalence. The first two are maybe 10 times slower than the fast solutions, and the last one is maybe 100 times slower.
以下解决方案不使用NSLiteralSearch
也不执行精确的逐字符等效。前两个可能比快速解决方案慢 10 倍,最后一个可能慢 100 倍。
5. Slow solution: adaptation of pierrot3887 answer
5.慢解决:pierrot3887答案的适配
Using scanUpToString:intoString:
. Too bad that NSScanner
doesn't offer an option for exact character-by-character equivalence.
使用scanUpToString:intoString:
. 太糟糕了,NSScanner
它没有提供精确的逐字符等效选项。
- (NSUInteger)countOccurrencesOfString:(NSString *)stringToFind
{
NSScanner *scanner = [NSScanner scannerWithString:self];
scanner.charactersToBeSkipped = nil;
scanner.caseSensitive = YES;
NSUInteger numberOfOccurrences = 0;
while (!scanner.isAtEnd) {
[scanner scanUpToString:stringToFind intoString:nil];
if (!scanner.isAtEnd) {
numberOfOccurrences++;
[scanner scanString:stringToFind intoString:nil];
}
}
return numberOfOccurrences;
}
6. Slower solution: gbaor solution
6.较慢的解决方案:gbaor解决方案
Using componentsSeparatedByString:
. Regarding the argument of doable in one line, note that the fastest solution given above is also a one liner.
使用componentsSeparatedByString:
. 关于 doable in one line 的论点,请注意上面给出的最快的解决方案也是 one liner。
- (NSUInteger)countOccurrencesOfString:(NSString *)stringToFind
{
return [self componentsSeparatedByString:stringToFind].count - 1;
}
7. Slowest solution: adaptation of vikingosegundo answer
7.最慢的解决方案:改编vikingosegundo答案
Using enumerateSubstringsInRange:options:usingBlock:
.
使用enumerateSubstringsInRange:options:usingBlock:
.
- (NSUInteger)countOccurrencesOfCharacter:(NSString *)characterToFind
{
__block NSUInteger counter = 0;
[self enumerateSubstringsInRange:NSMakeRange(0, self.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
if ([characterToFind isEqualToString:substring]) counter += 1;
}];
return counter;
}
回答by C?ur
The example with the Scanner was crashing on iPhone. I found this solution :
带有扫描仪的示例在 iPhone 上崩溃了。我找到了这个解决方案:
NSString *yourString = @"ABCCDEDRFFED"; // For example
NSScanner *mainScanner = [NSScanner scannerWithString:yourString];
NSString *temp;
NSInteger numberOfChar=0;
while(![mainScanner isAtEnd])
{
[mainScanner scanUpToString:@"C" intoString:&temp];
numberOfChar++;
[mainScanner scanString:@"C" intoString:nil];
}
It worked for me without crash. Hope it can help !
它对我有用,没有崩溃。希望它可以帮助!
回答by stefanB
I would probably use
我可能会用
NSString rangeOfCharacterFromSet:
NSString rangeOfCharacterFromSet:
or
或者
rangeOfCharacterFromSet:options:range::
rangeOfCharacterFromSet:options:range::
where the set is the set of characters you're searching for. It returns with the location of first character matching the set. Keep array or dictionary and increment the count for character, then repeat.
其中 set 是您要搜索的字符集。它返回与集合匹配的第一个字符的位置。保留数组或字典并增加字符的计数,然后重复。
回答by txaidw
Here is a Swift 3 working version, for NSRange, Range, String and NSString! Enjoy :)
这是一个 Swift 3 工作版本,适用于 NSRange、Range、String 和 NSString!享受 :)
/// All ranges using NSString and NSRange
/// Is usually used together with NSAttributedString
extension NSString {
public func ranges(of searchString: String, options: CompareOptions = .literal, searchRange: NSRange? = nil) -> [NSRange] {
let searchRange = searchRange ?? NSRange(location: 0, length: self.length)
let subRange = range(of: searchString, options: options, range: searchRange)
if subRange.location != NSNotFound {
let nextRangeStart = subRange.location + subRange.length
let nextRange = NSRange(location: nextRangeStart, length: searchRange.location + searchRange.length - nextRangeStart)
return [subRange] + ranges(of: searchString, options: options, searchRange: nextRange)
} else {
return []
}
}
}
/// All ranges using String and Range<Index>
/// Is usually used together with NSAttributedString
extension String {
public func ranges(of searchString: String, options: CompareOptions = [], searchRange: Range<Index>? = nil ) -> [Range<Index>] {
if let range = range(of: searchString, options: options, range: searchRange, locale: nil) {
let nextRange = range.upperBound..<(searchRange?.upperBound ?? endIndex)
return [range] + ranges(of: searchString, searchRange: nextRange)
} else {
return []
}
}
}