2014-09-29 3 views
10

Swift, похоже, пытается обесценить понятие строки, состоящей из массива атомных символов, что имеет смысл для многих применений, но есть очень много программирования, которое включает в себя сбор данных по объектам, которые являются ASCII для всех практических целей: особенно с файловыми вводами/выводами. Отсутствие встроенной функции языка, чтобы указать литеру кажется, зияющую дыру, т.е. нет аналога C/Java/и т.д.-эск:Есть ли чистый способ указать символьные литералы в Swift?

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

Это довольно неудобно, потому что даже если вы конвертировать строки в массивы символов, вы не можете сделать что-то вроде:

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

Один довольно Hacky обходного пути, чтобы сделать что-то вроде этого:

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

Это работает, но, очевидно, это довольно уродливо. Кто-нибудь имеет лучший способ?

ответ

10

Character s может быть создан от String до тех пор, пока те String s состоят только из одного символа. И, поскольку Character реализует ExtendedGraphemeClusterLiteralConvertible, Swift сделает это автоматически для вас при назначении. Таким образом, чтобы создать в Swift в Character, вы можете просто сделать что-то вроде:

let ch: Character = "a" 

Затем вы можете использовать contains метод в IntervalType (генерируемый с Range operators), чтобы проверить, если персонаж находится в пределах диапазона вы «повторно ищет:

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

Пример:

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

Выходы:

да


Update: Как @MartinR отметил, упорядоченность символов Swift основана на Unicode Normalization Form D, который в том же порядке, как коды ASCII символов не. В вашем конкретном случае есть больше символов между a и z, чем в прямом ASCII (например, ä). См. Ответ @ MartinR here для получения дополнительной информации.

Если вам нужно проверить, находится ли символ между двумя символьными кодами ASCII, вам может понадобиться сделать что-то вроде вашего обходного пути.Тем не менее, вы также должны преобразовать ch к unichar, а не Character для его работы (см this question для получения дополнительной информации о Character против 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") 
} 

Или, еще более многословным способом без использования 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") 
} 

Примечание: Оба эти примеры работают только тогда, когда символ содержит только одну кодовую точку, но, до тех пор, как мы ограничены A SCII, это не должно быть проблемой.

+0

Обратите внимание, что порядок строк или символов Swift не основан на коде ASCII (сравните http://stackoverflow.com/a/25775112/1187415). Например, '" a "..." z "' содержит символ "ä". Это может быть желательно или нет. –

+0

@MartinR Спасибо, я этого не осознал. Я обновил ответ с этой информацией. –

+0

Это хороший трюк, который я буду держать в банке для других видов логики. Тем не менее, это немного высокий уровень: когда дело доходит до синтаксического анализа формата char-by-char, где производительность имеет значение как точность, нет никакой возможности для того, чтобы быть в состоянии идти туда и обратно между int-like и строковым представлением характер. Так что char-literal все еще необходим. –

6

Если вам нужен C-стиле ASCII литералов, вы можете просто сделать это:

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

Или, если вам нужно 32-битные Unicode литералы вы можете сделать это:

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

или 16 -бит:

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

Все эти инициализаторы будут оцениваться во время компиляции, так что это действительно так пойте немедленный литерал на уровне инструкции сборки.

Смежные вопросы