Swift seems to be trying to deprecate the notion of a string being composed of an array of atomic characters, which makes sense for many uses, but there's an awful lot of programming that involves picking through datastructures that are ASCII for all practical purposes: particularly with file I/O. The absence of a built in language feature to specify a character literal seems like a gaping hole, i.e. there is no analog of the C/Java/etc-esque:
Swift似乎试图摒弃由原子字符数组组成的字符串的概念,这在很多情况下都是有意义的,但是有很多编程都涉及到从ASCII结构中挑选所有实际用途的数据结构:特别是对于文件I/O。在语言功能中没有一个内置的字符来指定一个字符,这看起来像是一个大洞,也就是说,没有类似于C/Java/etc的类似的东西:
String foo="a"
char bar='a'
This is rather inconvenient, because even if you convert your strings into arrays of characters, you can't do things like:
这很不方便,因为即使你把你的字符串转换成字符数组,你也做不到:
let ch:unichar = arrayOfCharacters[n]
if ch >= 'a' && ch <= 'z' {...whatever...}
One rather hacky workaround is to do something like this:
一个相当陈腐的变通方法是做这样的事情:
let LOWCASE_A = ("a" as NSString).characterAtIndex(0)
let LOWCASE_Z = ("z" as NSString).characterAtIndex(0)
if ch >= LOWCASE_A && ch <= LOWCASE_Z {...whatever...}
This works, but obviously it's pretty ugly. Does anyone have a better way?
这是可行的,但显然它很丑。有人有更好的办法吗?
2 个解决方案
#1
10
Character
s can be created from String
s as long as those String
s are only made up of a single character. And, since Character
implements ExtendedGraphemeClusterLiteralConvertible
, Swift will do this for you automatically on assignment. So, to create a Character
in Swift, you can simply do something like:
可以从字符串创建字符,只要这些字符串仅由单个字符组成。而且,由于字符实现了ExtendedGraphemeClusterLiteralConvertible, Swift将在分配时自动为您实现这个功能。因此,要想在Swift中创建一个角色,你可以简单地做一些事情:
let ch: Character = "a"
Then, you can use the contains
method of an IntervalType
(generated with the Range
operators) to check if a character is within the range you're looking for:
然后,您可以使用IntervalType的contains方法(与Range操作符一起生成)检查字符是否在您要查找的范围内:
if ("a"..."z").contains(ch) {
/* ... whatever ... */
}
Example:
例子:
let ch: Character = "m"
if ("a"..."z").contains(ch) {
println("yep")
} else {
println("nope")
}
Outputs:
输出:
yep
是的
Update: As @MartinR pointed out, the ordering of Swift characters is based on Unicode Normalization Form D which is not in the same order as ASCII character codes. In your specific case, there are more characters between a
and z
than in straight ASCII (ä
for example). See @MartinR's answer here for more info.
更新:正如@MartinR所指出的,Swift字符的排序是基于Unicode规范化的Form D,与ASCII字符代码的顺序不同。在您的具体示例中,a和z之间的字符比直接ASCII(例如a)中的字符要多。更多信息请参见@MartinR的答案。
If you need to check if a character is in between two ASCII character codes, then you may need to do something like your original workaround. However, you'll also have to convert ch
to an unichar
and not a Character
for it to work (see this question for more info on Character
vs unichar
):
如果您需要检查一个字符是否在两个ASCII字符代码之间,那么您可能需要做一些类似于原始工作的事情。但是,您还必须将ch转换成一个unichar,而不是一个用于它的字符(有关字符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")
}
Or, the even more verbose way without using NSString
:
或者,更详细的方式不用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")
}
Note: Both of those examples only work if the character only contains a single code point, but, as long as we're limited to ASCII, that shouldn't be a problem.
注意:这两个示例只在字符只包含一个代码点的情况下才有效,但是,只要我们仅限于ASCII,这就不是问题。
#2
7
If you need C-style ASCII literals, you can just do this:
如果你需要c风格的ASCII文字,你可以这样做:
let chr = UInt8(ascii:"A") // == UInt8( 0x41 )
Or if you need 32-bit Unicode literals you can do this:
或者如果你需要32位的Unicode字符,你可以这样做:
let unichr1 = UnicodeScalar("A").value // == UInt32( 0x41 )
let unichr2 = UnicodeScalar("é").value // == UInt32( 0xe9 )
let unichr3 = UnicodeScalar("????").value // == UInt32( 0x1f600 )
Or 16-bit:
或16位:
let unichr1 = UInt16(UnicodeScalar("A").value) // == UInt16( 0x41 )
let unichr2 = UInt16(UnicodeScalar("é").value) // == UInt16( 0xe9 )
All of these initializers will be evaluated at compile time, so it really is using an immediate literal at the assembly instruction level.
所有这些初始化器都将在编译时进行计算,因此它实际上是在汇编指令级别上使用直接的文字。
#1
10
Character
s can be created from String
s as long as those String
s are only made up of a single character. And, since Character
implements ExtendedGraphemeClusterLiteralConvertible
, Swift will do this for you automatically on assignment. So, to create a Character
in Swift, you can simply do something like:
可以从字符串创建字符,只要这些字符串仅由单个字符组成。而且,由于字符实现了ExtendedGraphemeClusterLiteralConvertible, Swift将在分配时自动为您实现这个功能。因此,要想在Swift中创建一个角色,你可以简单地做一些事情:
let ch: Character = "a"
Then, you can use the contains
method of an IntervalType
(generated with the Range
operators) to check if a character is within the range you're looking for:
然后,您可以使用IntervalType的contains方法(与Range操作符一起生成)检查字符是否在您要查找的范围内:
if ("a"..."z").contains(ch) {
/* ... whatever ... */
}
Example:
例子:
let ch: Character = "m"
if ("a"..."z").contains(ch) {
println("yep")
} else {
println("nope")
}
Outputs:
输出:
yep
是的
Update: As @MartinR pointed out, the ordering of Swift characters is based on Unicode Normalization Form D which is not in the same order as ASCII character codes. In your specific case, there are more characters between a
and z
than in straight ASCII (ä
for example). See @MartinR's answer here for more info.
更新:正如@MartinR所指出的,Swift字符的排序是基于Unicode规范化的Form D,与ASCII字符代码的顺序不同。在您的具体示例中,a和z之间的字符比直接ASCII(例如a)中的字符要多。更多信息请参见@MartinR的答案。
If you need to check if a character is in between two ASCII character codes, then you may need to do something like your original workaround. However, you'll also have to convert ch
to an unichar
and not a Character
for it to work (see this question for more info on Character
vs unichar
):
如果您需要检查一个字符是否在两个ASCII字符代码之间,那么您可能需要做一些类似于原始工作的事情。但是,您还必须将ch转换成一个unichar,而不是一个用于它的字符(有关字符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")
}
Or, the even more verbose way without using NSString
:
或者,更详细的方式不用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")
}
Note: Both of those examples only work if the character only contains a single code point, but, as long as we're limited to ASCII, that shouldn't be a problem.
注意:这两个示例只在字符只包含一个代码点的情况下才有效,但是,只要我们仅限于ASCII,这就不是问题。
#2
7
If you need C-style ASCII literals, you can just do this:
如果你需要c风格的ASCII文字,你可以这样做:
let chr = UInt8(ascii:"A") // == UInt8( 0x41 )
Or if you need 32-bit Unicode literals you can do this:
或者如果你需要32位的Unicode字符,你可以这样做:
let unichr1 = UnicodeScalar("A").value // == UInt32( 0x41 )
let unichr2 = UnicodeScalar("é").value // == UInt32( 0xe9 )
let unichr3 = UnicodeScalar("????").value // == UInt32( 0x1f600 )
Or 16-bit:
或16位:
let unichr1 = UInt16(UnicodeScalar("A").value) // == UInt16( 0x41 )
let unichr2 = UInt16(UnicodeScalar("é").value) // == UInt16( 0xe9 )
All of these initializers will be evaluated at compile time, so it really is using an immediate literal at the assembly instruction level.
所有这些初始化器都将在编译时进行计算,因此它实际上是在汇编指令级别上使用直接的文字。