NSArray de NSCharacterset

Atualmente eu sou capaz de fazer uma série de alfabetos como abaixo

[[NSArray alloc]initWithObjects:@"A",@"B",@"C",@"D",@"E",@"F",@"G",@"H",@"I",@"J",@"K",@"L",@"M",@"N",@"O",@"P",@"Q",@"R",@"S",@"T",@"U",@"V",@"W",@"X",@"Y",@"Z",nil]; 

Sabendo que está disponível ao longo

 [NSCharacterSet uppercaseLetterCharacterSet] 

Existe alguma maneira eu posso fazer um arranjo com isso?

O código a seguir cria uma matriz contendo todos os caracteres de um determinado conjunto de caracteres. Funciona também para personagens fora do “plano multilingue básico” (caracteres> U + FFFF, por exemplo, U + 10400 DESENHO CAPITAL CARTA LONG I).

 NSCharacterSet *charset = [NSCharacterSet uppercaseLetterCharacterSet]; NSMutableArray *array = [NSMutableArray array]; for (int plane = 0; plane < = 16; plane++) { if ([charset hasMemberInPlane:plane]) { UTF32Char c; for (c = plane << 16; c < (plane+1) << 16; c++) { if ([charset longCharacterIsMember:c]) { UTF32Char c1 = OSSwapHostToLittleInt32(c); // To make it byte-order safe NSString *s = [[NSString alloc] initWithBytes:&c1 length:4 encoding:NSUTF32LittleEndianStringEncoding]; [array addObject:s]; } } } } 

Para o uppercaseLetterCharacterSet isso fornece uma matriz de 1467 elementos. Mas observe que os caracteres> U + FFFF são armazenados como par substituto UTF-16 em NSString , portanto, por exemplo, U + 10400 é armazenado em NSString como 2 caracteres "\ uD801 \ uDC00".

O código Swift 2 pode ser encontrado em outras respostas a essa pergunta. Aqui está uma versão do Swift 3 , escrita como um método de extensão:

 extension CharacterSet { func allCharacters() -> [Character] { var result: [Character] = [] for plane: UInt8 in 0...16 where self.hasMember(inPlane: plane) { for unicode in UInt32(plane) < < 16 ..< UInt32(plane + 1) << 16 { if let uniChar = UnicodeScalar(unicode), self.contains(uniChar) { result.append(Character(uniChar)) } } } return result } } 

Exemplo:

 let charset = CharacterSet.uppercaseLetters let chars = charset.allCharacters() print(chars.count) // 1521 print(chars) // ["A", "B", "C", ... "] 

(Observe que alguns caracteres podem não estar presentes na fonte usada para exibir o resultado.)

Como os caracteres têm um alcance limitado e finito (e não muito amplo), é possível testar quais caracteres são membros de um determinado conjunto de caracteres (força bruta):

 // this doesn't seem to be available #define UNICHAR_MAX (1ull < < (CHAR_BIT * sizeof(unichar))) NSData *data = [[NSCharacterSet uppercaseLetterCharacterSet] bitmapRepresentation]; uint8_t *ptr = [data bytes]; NSMutableArray *allCharsInSet = [NSMutableArray array]; // following from Apple's sample code for (unichar i = 0; i < UNICHAR_MAX; i++) { if (ptr[i >> 3] & (1u < < (i & 7))) { [allCharsInSet addObject:[NSString stringWithCharacters:&i length:1]]; } } 

Eu criei uma versão Swift (v2.1) do algoritmo de Martin R:

 let charset = NSCharacterSet.URLPathAllowedCharacterSet(); for var plane : UInt8 in 0...16 { if charset.hasMemberInPlane( plane ) { var c : UTF32Char; for var c : UInt32 = UInt32( plane ) < < 16; c < (UInt32(plane)+1) << 16; c++ { if charset.longCharacterIsMember(c) { var c1 = c.littleEndian // To make it byte-order safe let s = NSString(bytes: &c1, length: 4, encoding: NSUTF32LittleEndianStringEncoding); NSLog("Char: \(s)"); } } } } 

Isso é feito usando um pouco mais de swift rápido.

 let characters = NSCharacterSet.uppercaseLetterCharacterSet() var array = [String]() for plane: UInt8 in 0...16 where characters.hasMemberInPlane(plane) { for character: UTF32Char in UInt32(plane) < < 16..<(UInt32(plane) + 1) << 16 where characters.longCharacterIsMember(character) { var endian = character.littleEndian let string = NSString(bytes: &endian, length: 4, encoding: NSUTF32LittleEndianStringEncoding) as! String array.append(string) } } print(array) 

Você não deveria; este não é o propósito de um conjunto de caracteres. Um NSCharacterSet é um conjunto de caracteres possivelmente infinito, possivelmente em pontos de código ainda não inventados. Tudo o que você quer saber é “Este personagem ou coleção de personagens neste conjunto?”, E para esse fim é útil.

Imagine este código Swift:

 let asciiCodepoints = Unicode.Scalar(0x00)...Unicode.Scalar(0x7F) let asciiCharacterSet = CharacterSet(charactersIn: asciiCodepoints) let nonAsciiCharacterSet = asciiCharacterSet.inverted 

O que é análogo a este código Objective-C:

 NSRange asciiCodepoints = NSMakeRange(0x00, 0x7F); NSCharacterSet * asciiCharacterSet = [NSCharacterSet characterSetWithRange:asciiCodepoints]; NSCharacterSet * nonAsciiCharacterSet = asciiCharacterSet.invertedSet; 

É fácil dizer “loop sobre todos os caracteres em asciiCharacterSet “; isso apenas passaria por todos os personagens de U+0000 até U+007F . Mas o que significa fazer um loop sobre todos os caracteres em nonAsciiCharacterSet ? Você começa em U+0080 ? Quem pode dizer que não haverá pontos de código negativos no futuro? Onde você termina? Você pula caracteres não imprimíveis? E quanto aos clusters de grafemas estendidos? Como é um conjunto (onde a ordem não importa), seu código pode manipular pontos de código fora de ordem nesse loop?

Estas são perguntas que você não quer responder aqui; funcionalmente nonAsciiCharacterSet é infinito, e tudo que você quer usar é dizer se algum dado personagem está fora do conjunto de caracteres ASCII.


A questão que você realmente deveria estar se perguntando é: “O que eu quero realizar com este conjunto de letras maiúsculas?” Se (e provavelmente somente se) você realmente precisar iterar sobre ele em ordem, colocar os que você gosta em uma Array ou String (talvez uma leitura de um arquivo de recurso) é provavelmente a melhor maneira. Se você quiser verificar se um caractere faz parte do conjunto de letras maiúsculas, então você não se importa com a ordem ou mesmo quantos caracteres estão no conjunto , e deve usar CharacterSet.uppercaseLetters.contains(foo) (em Objetivo-C: [NSCharacterSet.uppercaseLetterCharacterSet contains: foo] ).

Pense também em caracteres não latinos. CharacterSet.uppercaseLetters abrange categorias gerais de Unicode Lu e Lt , que contêm A a Z e também coisas como Dž , 𝕹 e Խ . Você não quer ter que pensar sobre isso. Você definitivamente não deseja emitir uma atualização para seu aplicativo quando o Unicode Consortium adicionar novos caracteres a essa lista. Se o que você quer fazer é decidir se algo é maiúsculo, não se preocupe em codificar nada.

Por apenas AZ do alfabeto latino (nada com grego, ou marcas diacríticas, ou outras coisas que não eram o que o cara pediu):

 for plane: UInt8 in 0...16 where characters.hasMemberInPlane(plane) { i = 0 for character: UTF32Char in UInt32(plane) < < 16...(UInt32(plane) + 1) << 16 where characters.longCharacterIsMember(character) { var endian = character.littleEndian let string = NSString(bytes: &endian, length: 4, encoding: NSUTF32LittleEndianStringEncoding) as! String array.append(string) if(array.count == 26) { break } } if(array.count == 26) { break } } 

Eu achei a solução do Martin R muito lenta para os meus propósitos, então resolvi de outra maneira usando a propriedade bitmapRepresentation do bitmapRepresentation .

Isso é significativamente mais rápido de acordo com meus benchmarks:

 var ranges = [CountableClosedRange]() let bitmap: Data = characterSet.bitmapRepresentation var first: UInt32?, last: UInt32? var plane = 0, nextPlane = 8192 for (j, byte) in bitmap.enumerated() where byte != 0 { if j == nextPlane { plane += 1 nextPlane += 8193 continue } for i in 0 ..< 8 where byte & 1 << i != 0 { let codePoint = UInt32(j - plane) * 8 + UInt32(i) if let _last = last, codePoint == _last + 1 { last = codePoint } else { if let first = first, let last = last { ranges.append(first ... last) } first = codePoint last = codePoint } } } if let first = first, let last = last { ranges.append(first ... last) } return ranges 

Essa solução retorna uma matriz de intervalos codePoint, mas você pode adaptá-la facilmente para retornar caracteres individuais ou cadeias de caracteres, etc.