はじめに
Objective-Cでは文字列はNSString
を利用し、その長さを取得するにはlength
メソッドを使うのが一般的です。
ですが、これだと昨今頻繁に使われている絵文字などが混在した場合に"見た目通りの"長さが取得できません。
NSString *text = @"あ🌟い🐶う🐱え✪お☆";
NSLog(@"length: %@", @(text.length));
//=> length: 13
絵文字入りの場合はenumerateSubstringsInRangeを使う
絵文字が混在している場合はenumerateSubstringsInRange
をNSStringEnumerationByComposedCharacterSequences
オプションで利用することで"見た目通りの"長さが取得できます。
NSString
のカテゴリとしてメソッドとプロパティを定義してあげると使いやすいと思います。
#import <Foundation/Foundation.h>
@interface NSString (GraphemeLength)
/**
* 人間の見た目通りの文字数を返す。
* 絵文字などNSStringのlengthでは2文字扱いになってしまう文字を含む文字列向け。
*/
@property (assign, nonatomic, readonly) NSInteger graphemeLength;
/**
* 人間の見た目通りの文字数を返す。
* 絵文字などNSStringのlengthでは2文字扱いになってしまう文字を含む文字列向け。
*
* @return 見た目通りの文字数
*/
- (NSInteger)graphemeLength;
@end
#import "NSString+GraphemeLength.h"
@implementation NSString (GraphemeLength)
- (NSInteger)getGraphemeLength {
return [self graphemeLength];
}
- (NSInteger)graphemeLength {
__block NSInteger length = 0;
[self enumerateSubstringsInRange:NSMakeRange(0, [self length])
options:NSStringEnumerationByComposedCharacterSequences
usingBlock: ^(NSString* substring, NSRange substringRange, NSRange enclosingRange, BOOL* stop) {
length++;
}];
return length;
}
@end
使用例
NSString *text = @"あ🌟い🐶う🐱え✪お☆";
NSLog(@"length: %@", @(text.graphemeLength));
//=> length: 10
追記
コメント欄にてご指摘がありました。
NSStringEnumerationByComposedCharacterSequences
は一部の絵文字(国旗の絵文字(🇯🇵など))は2文字としてカウントしてしまうため、上記の方法では完全にSwiftのcount
メソッド(Swift1.1ではcountElements
関数、Swift1.2ではcount
関数)と同等の結果は得られないようです。
追記(2016/11/18)
こちらの記事を読んでいて気づいたのですが、現状仕様が変わっているようです。
Swift3.0で絵文字かどうかを判定するスマートな方法を考えた
NSStringEnumerationByComposedCharacterSequences
を利用した場合、🇯🇵や👩👩👧👦なども1文字としてカウントするようになっていました。
また、当時試していなかったのですが、少なくともSwift 3.0では、👩👩👧👦は characters.count
で4文字とカウントされるようです。
おまけ
SwiftではcountElements
やcount
で一発です・ω・
追記したとおり、👩👩👧👦など、1文字としてカウントされない絵文字があります。
let text = "あ🌟い🐶う🐱え✪お☆";
println("length: \(countElements(text))")
//=> length: 10
let text = "あ🌟い🐶う🐱え✪お☆";
println("length: \(count(text))")
//=> length: 10
let text = "あ🌟い🐶う🐱え✪お☆";
print("length: \(text.characters.count)")
//=> length: 10