2014-09-29 14 views
10

Swift wydaje się próbować wycofać pojęcie ciągu składającego się z szeregu atomowych znaków, co ma sens dla wielu zastosowań, ale jest bardzo dużo programowania, które polega na wybieraniu przez struktury danych ASCII dla wszystkich praktycznych zastosowań: szczególnie w przypadku plików I/O. Nieobecność wbudowane funkcje języka, aby określić charakter dosłowne wydaje się dziura, to znaczy nie ma analogiem C/Java/etc bajek Disneya:Czy istnieje czysty sposób określania literałów znaków w Swift?

String foo="a" 
char bar='a' 

Jest to dość niewygodne, ponieważ nawet jeśli konwertowanie ciągów znaków na tablicach, nie można robić takie rzeczy jak:

let ch:unichar = arrayOfCharacters[n] 
if ch >= 'a' && ch <= 'z' {...whatever...} 

One raczej hacky obejście jest zrobić coś takiego:

let LOWCASE_A = ("a" as NSString).characterAtIndex(0) 
let LOWCASE_Z = ("z" as NSString).characterAtIndex(0) 
if ch >= LOWCASE_A && ch <= LOWCASE_Z {...whatever...} 

to działa, ale oczywiście to jest dość brzydkie. Czy ktoś ma lepszy sposób?

Odpowiedz

10

Character s można tworzyć z String s tak długo, jak te String s składają się tylko z jednego znaku. I od Character implementuje ExtendedGraphemeClusterLiteralConvertible, Swift zrobi to automatycznie dla ciebie przy wyznaczaniu. Tak, aby utworzyć Character w Swift, można po prostu zrobić coś takiego:

let ch: Character = "a" 

Następnie można zastosować metodę z IntervalType (generowane z Range operators) contains aby sprawdzić, czy znak jest w przedziale wy szukasz:

if ("a"..."z").contains(ch) { 
    /* ... whatever ... */ 
} 

przykład:

let ch: Character = "m" 
if ("a"..."z").contains(ch) { 
    println("yep") 
} else { 
    println("nope") 
} 

wyjść:

yep


Aktualizacja: W @MartinR wskazał, kolejność znaków Swift jest oparta na Unicode Normalization Form D który jest nie w tej samej kolejności co kodów ASCII. W twoim konkretnym przypadku istnieje więcej znaków między a i z niż w prostym ASCII (na przykład ä). Aby uzyskać więcej informacji, zobacz odpowiedź @ MartinR na numer here.

Aby sprawdzić, czy znak znajduje się pomiędzy dwoma kodami znaków ASCII, konieczne może być zrobienie czegoś w rodzaju oryginalnego obejścia.Jednakże, będziesz też musiał konwertować ch do unichar a nie jako Character go do pracy (patrz this question uzyskać więcej informacji na Character vs unichar):

let a_code = ("a" as NSString).characterAtIndex(0) 
let z_code = ("z" as NSString).characterAtIndex(0) 
let ch_code = (String(ch) as NSString).characterAtIndex(0) 

if (a_code...z_code).contains(ch_code) { 
    println("yep") 
} else { 
    println("nope") 
} 

Lub jeszcze bardziej rozwlekły sposób bez użycia NSString :

let startCharScalars = "a".unicodeScalars 
let startCode = startCharScalars[startCharScalars.startIndex] 

let endCharScalars = "z".unicodeScalars 
let endCode = endCharScalars[endCharScalars.startIndex] 

let chScalars = String(ch).unicodeScalars 
let chCode = chScalars[chScalars.startIndex] 

if (startCode...endCode).contains(chCode) { 
    println("yep") 
} else { 
    println("nope") 
} 

Uwaga: Oba te przykłady działają tylko wtedy, gdy znak zawiera tylko jeden punkt kodu, ale tak długo, jak mamy ograniczone do SCII, to nie powinno stanowić problemu.

+0

Pamiętaj, że porządkowanie ciągów lub znaków Swift nie opiera się na kodzie ASCII (porównaj http://stackoverflow.com/a/25775112/1187415). Np. '" A "..." z "' zawiera znak "ä". To może być pożądane lub nie. –

+0

@MartinR Dzięki, nie zdawałem sobie z tego sprawy. Zaktualizowałem odpowiedź tymi informacjami. –

+0

To miła sztuczka, którą zatrzymam w banku dla innych rodzajów logiki. Wciąż nieco na wysokim poziomie: jeśli chodzi o parsowanie formatu pliku char-by-char, gdzie wydajność ma znaczenie tak samo precyzyjne, nie ma możliwości zastąpienia go przez int-like i string-like postać. Tak więc literał dosłownie nadal jest potrzebny. –

6

Jeśli potrzebujesz stylu C literały ASCII, można po prostu to zrobić:

let chr = UInt8(ascii:"A") // == UInt8(0x41) 

Lub jeśli potrzebujesz 32-bitowe literałów Unicode można to zrobić:

let unichr1 = UnicodeScalar("A").value // == UInt32(0x41) 
let unichr2 = UnicodeScalar("é").value // == UInt32(0xe9) 
let unichr3 = UnicodeScalar("").value // == UInt32(0x1f600) 

lub 16 -bit:

let unichr1 = UInt16(UnicodeScalar("A").value) // == UInt16(0x41) 
let unichr2 = UInt16(UnicodeScalar("é").value) // == UInt16(0xe9) 

Wszystkie te inicjatory zostaną ocenione podczas kompilacji, więc to naprawdę jest śpiewać natychmiastową literał na poziomie instrukcji montażu.

Powiązane problemy