# Swift中文教程(三) 字符串和字符

由苹果官网翻译得来
fork自https://github.com/letsswift/The-Swift-Programming-Language-in-Chinese
https://github.com/TyrantDante/The-Swift-Programming-Language-in-Chinese 完善和检查

一个字符串String就是一个字符序列,像”hello,world”,”albatross”这样的。Swift中的字符串是用String关键词来定义的,同时它也是一些字符的集合,用Character定义。

Swift的String和Character类型为代码提供了一个快速的,兼容Unicode的字符解决方案。String类型的初始化和使用都是可读的,并且和C中的strings类似。同时String也可以通过使用+运算符来组合,使用字符串就像使用Swift中的其他基本类型一样简单。

字符串常量

在代码中可以使用由String预先定义的字符串常量,定义方式非常简单:

let someString = “Some string literal value”

字符串常量可以包括下面这些特殊字符:

  • 空字符\0,反斜杠\,制表符\t,换行符\n,回车符\r,双引号\”和单引号\’

  • 单字节Unicode字符,\xnn,其中nn是两个十六进制数

  • 双字节Unicode字符,\unnnn,其中nnnn是四个十六进制数

  • 四字节Unicode字符,\Unnnnnnnn,其中nnnnnnnn是八个十六进制数

下面的代码给出了这四种字符串的例子:

let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
// "Imagination is more important than knowledge" - Einstein
let dollarSign = "\x24" // $, Unicode scalar U+0024
let blackHeart = "\u2665" // ♥, Unicode scalar U+2665
let sparklingHeart = "\U0001F496" // , Unicode scalar U+1F496

初始化一个空串

初始化一个空串时有两种形式,但是两种初始化方法的结果都一样,表示空串

var emptyString = "" // empty string literal
var anotherEmptyString = String() // initializer syntax
// these two strings are both empty, and are equivalent to each other

通过isEmpty属性可以检查一个字符串是否为空

if emptyString.isEmpty {
    print("Nothing to see here")
}
// prints "Nothing to see here"

变长字符串

如果使用var关键词定义的字符串即为可修改的变长字符串,而let关键词定义的字符串是常量字符串,不可修改。

var variableString = "Horse"
variableString += " and carriage"
// variableString is now "Horse and carriage"
let constantString = "Highlander"
constantString += " and another Highlander"
// this reports a compile-time error - a constant string cannot be modified

字符串不是指针,而是实际的值

在Swift中,一个String类型就是一个实际的值,当定义一个新的String,并且将之前的String值拷贝过来的时候,是实际创建了一个相等的新值,而不是仅仅像指针那样指向过去。

同样在函数传递参数的时候,也是传递的实际值,并且创建了一个新的字符串,后续的操作都不会改变原有的String字符串。

字符

Swift的字符串String就是由字符Character组成的,每一个Character都代表了一个特定的Unicode字符。通过for-in循环,可以遍历字符串中的每一个字符:

for character in "Dog!🐶" {
    print(character)
}
// D
// o
// g
// !
// 🐶

你也可以仅仅定义一个单独的字符:

let yenSign: Character = "¥"

String 值可以通过传递一个字符数组来初始化

let catCharacters:[Character] = ["C","a","t","!","🐱"]
let catString = String(carCharacters)
print(catString)
//Prints "Cat!🐱"

字符计数

使用全局函数countElements可以计算一个字符串中字符的数量:

let unusualMenagerie = "Koala , Snail , Penguin , Dromedary "
print("unusualMenagerie has \(countElements(unusualMenagerie)) characters")
// prints "unusualMenagerie has 40 characters"

组合使用字符和字符串

String和Character类型可以通过使用+号相加来组合成一个新的字符串

let string1 = "hello"
let string2 = " there"
let character1: Character = "!"
let character2: Character = "?"
let stringPlusCharacter = string1 + character1 // equals "hello!"
let stringPlusString = string1 + string2 // equals "hello there"
let characterPlusString = character1 + string1 // equals "!hello"
let characterPlusCharacter = character1 + character2 // equals "!?"

也可以使用+=号来组合:

var instruction = "look over"
instruction += string2
// instruction now equals "look over there"
var welcome = "good morning"
welcome += character1
// welcome now equals "good morning!"

使用字符串生成新串

通过现有的字符串,可以使用如下方法来生成新的字符串:

let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message is "3 times 2.5 is 7.5"

在上面这个例子中,首先使用multiplier这个字符串3,来作为新串的一部分,用(multiplier)添加,同时上面的例子还用到了类型转换Double(multiplier),将计算结果和字符串本身都作为元素添加到了新的字符串中。

大小写字符串

你可以从一个String类型的uppercaseString 和 lowercaseString中获得一个字符串的大写或小写。

let normal = "Could you help me, please?"
let shouty = normal.uppercaseString
// shouty is equal to "COULD YOU HELP ME, PLEASE?"
let whispered = normal.lowercaseString
// whispered is equal to "could you help me, please?"

Unicode

Unicode是编码和表示文本的国际标准。它几乎可以显示所有语言的所有字符的标准形态。还可以从类似于文本文件或者网页这样的外部源文件中读取和修改他们的字符。

Unicode术语

每一个Unicode字符都能被编码为一个或多个unicode scalar。一个unicode scalar是一个唯一的21位数(或者名称),对应着一个字符或者标识。例如 U+0061是一个小写的A (“a”), 或者U+1F425是一个面向我们的黄色小鸡

当一个Unicode字符串写入文本或者其他储存时,unicode scalar会根据Unicode定义的格式来编码。每一个格式化编码字符都是小的代码块,称成为code units.他包含UTF-8格式(每一个字符串由8位的code units组成)。和UTF-16格式(每一个字符串由16位的code units组成)

Unicode字符串

Swift 支持多种不同的方式取得Unicode字符串.

你可以使用for-in语句遍历字符串,来获得每一个字符的Unicode编码值。这个过程已经在字符(Working with Characters)描述过了。

或者,下面三个描述中使用合适的一个来获得一个字符串的值

  • UTF-8字符编码单元集合使用String类型的utf-8属性
  • UTF-16字符编码单元集合使用String类型的utf-16属性
  • 21位Unicode标量集合使用String类型的unicodeScalars属性

下面的每一个例子展示了不同编码显示由 D , o , g , !

(DOG FACE, 或者Unicode标量 U+1F436)字符组成的字符串

UTF-8

你可以使用String类型的utf8属性遍历一个UTF-8编码的字符串。这个属性是UTF8View类型
,UTF8View是一个8位无符号整形(UInt8)的集合,集合中的每一个字节都是UTF-8编码。

for codeUnit in dogString.utf8 {
    print("\(codeUnit) ")
}
print("\n")
// 68 111 103 33 240 159 144 182

在上面的例子中,前4个十进制codeunit值(68,111,103,33)显示为字符串 D , o ,g 和 ! ,和他们的ASCII编码相同一样。后面4个codeunit的值(240,159,144,182)是DOG FACE字符的4字节UTF-8编码。

UTF-16

你可以使用String类型的utf16属性遍历一个UTF-16编码的字符串。这个属性是UTF16View类型,UTF16View是一个16位无符号整形(UInt16)的集合,集合中的每一个字节都是UTF-16编码。

for codeUnit in dogString.utf16 {
    print("\(codeUnit) ")
}
print("\n")
// 68 111 103 33 55357 56374

同理,前4个十进制codeunit值(68,111,103,33)显示为字符串 D , o ,g 和 ! ,他们的UTF-16 的codeunit和他们UTF-8的编码值相同。

第5和第6个codeunit值(55357和56374)是DOG FACE字符的UTF-16的代理对编码。他们的值是由值为U+D83D(十进制55357)的高位代理(lead surrogate)和值为U+DC36 (十进制56374)的低位代理(trail surrogate)组成。

Unicode标量

你可以使用String类型的unicodeScalars属性遍历一个Unicode标量编码的字符串。这个属性是UnicodeScalarsView类型,UnicodeScalarsView是一个UnicodeScalar类型的集合。每一个Unicode标量都是一个任意21位Unicode码位,没有高位代理,也没有低位代理。

每一个UnicodeScalar使用value属性,返回标量的21位值,每一位都是32位无符号整形(UInt32)的值:

for scalar in dogString.unicodeScalars {
    print("\(scalar.value) ")
}
print("\n")
// 68 111 103 33 128054

value属性在前4个UnicodeScalar值(68,111,103,33)再一次展示编码了字符 D , o , g 和 ! 。第五个也是最后一个UnicodeScalar 是DOG FACE字符,十进制为128054,等价于16进制的1F436,相当于Unicode标量的U+1F436。

每一个UnicodeScalar可以被构造成一个新的字符串来代替读取他们的value属性,类似于插入字符串。

for scalar in dogString.unicodeScalars { print("\(scalar) ") }
// D
// o
// g
// !
//

访问和修改string

我们可以通过string的方法和属性来访问和修改string,或者下目标语法

string目录

每个string值都一个序列,string.index。他可以用来定位string中的每一个字符

不同的字符占用了不同的内存,为了确定每个字符所在的位置,你必须从string的开头或结尾�遍历每个unicode数量。基于这么原因,swift string不能被integer定位。

使用startIndex属性来访问string中的第一个位置的字符。用endIndex是string中最后一个字符的后面。所以,endIndex不是一个有效的下标参数。如果string为空,那么startIndex和endIndex是一样的。

String.Index可以使用 predecessor()来获取前一个标签,successor()来获取下一标签。String中的index可以通过方法从别的index获取。或使用advanceBy(_:)。如果超出了访问一个string外的index会抛出一个运行时错误。

可以通过下标来访问string中特定的index来获取字符

let greeting = "Guten Tag!"
greeting[greeting.startIndex]
//G
greeting[greeting.endIndex.predecessor()]
//!
greeting[greeting.start.successor()]
//u
let index = greeting.startIndex.advanceBy(7)
greeting[index]
//a

尝试访问一个string范围以外的index,会抛出运行时错误
greeting[greeting.endIndex] //error
greeting.endIndex.successor() //error

使用characters的indices来创建每个indexes的rang,然后对他们都转成string输出

for index in greeting.characters.indices {
    print("\(greeting[index])",terminator:"")
}
//prints "G u t e n  T a g ! "

插入和删除

对string在一个特定的位置插入一个字符,使用 inset(_:atIndex:)方法

var welcome = "hello"
welcome.insert("!",atIndex:welcome.endIndex)
// welcome now equals "hello!"

对string在一个特定的位置插入另一个string的全部内容,使用 insertContentsOf(_:at:)方法

welcome.insetContentsOf(" there".characters , at:welcome.endIndex.predecessor())
// welcome now equals "hello there!"

删除string的一个特定位置的字符,使用 removeAtIndex(_:)方法

welcome.removeAtIndex(welcome.endIndex.predecessor())
//welcome now equals "hello there"

对于string的特定范围删除,使用removeRange(_:)方法

let rang = welcome.endIndex.advancedBy(-6)..<welcome.endIndex
welcome.removeRange(range)
//welcome now equals "hello"

字符串比较

Swift提供三种方法比较字符串的值:字符串相等,前缀相等,和后缀相等

字符串相等

当两个字符串的包含完全相同的字符时,他们被判断为相等。

let quotation = "We're a lot alike, you and I."
let sameQuotation = "We're a lot alike, you and I."
if quotation == sameQuotation {
    print("These two strings are considered equal")
}
// prints "These two strings are considered equal"
//输出”These two strings are considered equal”

前缀(prefix)相等和后缀(hasSuffix)相等

使用string 类的两个方法hasPrefix和hasSuffix,来检查一个字符串的前缀或者后缀是否包含另外一个字符串,它需要一个String类型型的参数以及返回一个布尔类型的值。两个方法都会在原始字符串和前缀字符串或者后缀字符串之间做字符与字符之间的。

下面一个例子中,用一个字符串数组再现了莎士比亚的罗密欧与朱丽叶前两幕的场景。

let romeoAndJuliet = [
    "Act 1 Scene 1: Verona, A public place",
    "Act 1 Scene 2: Capulet's mansion",
    "Act 1 Scene 3: A room in Capulet's mansion",
    "Act 1 Scene 4: A street outside Capulet's mansion",
    "Act 1 Scene 5: The Great Hall in Capulet's mansion",
    "Act 2 Scene 1: Outside Capulet's mansion",
    "Act 2 Scene 2: Capulet's orchard",
    "Act 2 Scene 3: Outside Friar Lawrence's cell",
    "Act 2 Scene 4: A street in Verona",
    "Act 2 Scene 5: Capulet's mansion",
    "Act 2 Scene 6: Friar Lawrence's cell"
]

你可以使用hasPrefix 方法和romeoAndJuliet数组 计算出第一幕要表演多少个场景。

var act1SceneCount = 0
for scene in romeoAndJuliet {
    if scene.hasPrefix("Act 1 ") {
        ++act1SceneCount
    }
}
print("There are \(act1SceneCount) scenes in Act 1")
//输出”There are 5 scenes in Act 1”

同理,使用hasSuffix 方法去计算有多少个场景发生在Capulet公馆和Friar Lawrence牢房

var mansionCount = 0
var cellCount = 0
for scene in romeoAndJuliet {
    if scene.hasSuffix("Capulet's mansion") {
        ++mansionCount
    } else if scene.hasSuffix("Friar Lawrence's cell") {
        ++cellCount
    }
}
print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
// 输出 "6 mansion scenes; 2 cell scenes”
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容