その後のその後

iOSエンジニア 堤 修一のブログ github.com/shu223

NSStringの全角/半角バリデーションチェック

某アプリにクレジットカード決済をつける際に、カード名義人入力欄で全角入力をはじく必要があり、ググってみたところ意外と「全角文字と半角文字を判定する方法」はあんまり出てこなかったのでこちらに書いておきます。
(全角→半角の変換の話はいっぱい出てきた)




結論として、下記コードのように、
1文字ずつURLエンコードし、文字列長が4以上だったら全角文字と判定する
という方法でうまくいきました。

/* URLエンコード */
-(NSString *)stringByURLEncoding:(NSStringEncoding)encoding
{
  NSArray *escapeChars = [NSArray arrayWithObjects:
             @";" ,@"/" ,@"?" ,@":"
            ,@"@" ,@"&" ,@"=" ,@"+"
            ,@"$" ,@"," ,@"[" ,@"]"
            ,@"#" ,@"!" ,@"'" ,@"("
            ,@")" ,@"*"
            ,nil];
 
  NSArray *replaceChars = [NSArray arrayWithObjects:
              @"%3B" ,@"%2F" ,@"%3F"
             ,@"%3A" ,@"%40" ,@"%26"
             ,@"%3D" ,@"%2B" ,@"%24"
             ,@"%2C" ,@"%5B" ,@"%5D"
             ,@"%23" ,@"%21" ,@"%27"
             ,@"%28" ,@"%29" ,@"%2A"
             ,nil];
 
  NSMutableString *encodedString = [[[self stringByAddingPercentEscapesUsingEncoding:encoding] mutableCopy] autorelease];
 
  for(int i=0; i<[escapeChars count]; i++) {
    [encodedString replaceOccurrencesOfString:[escapeChars objectAtIndex:i]
                                   withString:[replaceChars objectAtIndex:i]
                                      options:NSLiteralSearch
                                        range:NSMakeRange(0, [encodedString length])];
  }
 
  return [NSString stringWithString: encodedString];
}

/* 入力文字列を1文字ずつURLエンコードし、文字列長が4以上だったら全角文字と判定 */
+ (BOOL)isOnlyHalf:(NSString *)str {
	for(int i=0; i<[str length]; i++) {
		NSString *aChar = [str substringWithRange:NSMakeRange(i, 1)];
		NSString *encodedChar = [aChar stringByURLEncoding:NSUTF8StringEncoding];
		if ([encodedChar length] < 4) {
			continue;
		}
		else {
			return NO;
		}
	}
	return YES;
}

うまくいかなかった方法その1

ちなみに、NSCharacterSetで英数字のセットを作って判定する方法を最初に試したのですが、うまくいきませんでした。

/* 入力文字列が半角英数字と半角スペースのいずれかの文字を含む場合はYESを返す */
+ (BOOL)isHalfChar:(NSString *)str {
	NSMutableCharacterSet *charSet = [NSMutableCharacterSet alphanumericCharacterSet];
	[charSet formUnionWithCharacterSet:[NSCharacterSet whitespaceCharacterSet]];
	NSRange range = [str rangeOfCharacterFromSet:charSet];
	if (range.location == NSNotFound) {
		return NO;
	}
	return YES;
}

/* 1文字ずつisHalfCharで判定を行う */
+ (BOOL)isOnlyHalf:(NSString *)str {
	for (int i=0; i<[str length]; i++) {
		NSString *aChar = [str substringWithRange:NSMakeRange(i, 1)];
		if (![Hoge isHalfChar:aChar]) {
			return NO;
		}
	}
	
	return YES;
}


全角文字を入れても、rangeOfCharacterFromSet:で、先頭文字が検索結果として返ってきてしまい、NG。


うまくいかなかった方法その2

全角文字を文字コードで指定して判定する方法もうまくいきませんでした。

/* 全角文字のいずれかであればNOを返す(半角カナはYESを返す)*/
+ (BOOL) isHalfChar:(NSString *)aValue {
	NSRange match = [aValue rangeOfString:@"[\x20-\x7E\xA1-\xDF]" options:NSRegularExpressionSearch];
	if (match.location != NSNotFound) {
		return YES;
	}
	return NO;
}

/* 1文字ずつisHalfCharで判定を行う */
+ (BOOL)isOnlyHalf:(NSString *)str {
	for (int i=0; i<[str length]; i++) {
		NSString *aChar = [str substringWithRange:NSMakeRange(i, 1)];
		if (![Hoge isHalfChar:aChar]) {
			return NO;
		}
	}
	
	return YES;
}


英数字のセットを判定する方法と同様、全角文字を入れても、rangeOfString:で、NSNotFoundにならず、NG。
(いや、ちがう挙動だったかも。とにかくNGでした)




というわけで、結論として一番上の、「エンコードして文字列長を判定する」方法をご使用ください。