美文网首页Swift 学习日记
Swift 中 NSString 与 String 的认识

Swift 中 NSString 与 String 的认识

作者: tp夕阳武士 | 来源:发表于2018-03-15 11:36 被阅读193次

    在Swift语法中相对OC新增了一个修辞词:String,它与原来的NSString可以很方便的转换,但是在实际的开发过程中我们应该如何选择?

    1.能用String的时候就使用String

    (1).因为现在所有的Cocoa框架都能接收和也能返回String类型,所以没有必要特地转换
    (2).Swift中String是struct 而 NSString是NSObject,所以String更符合字符串不变的特性;同时在不触及NSString的特有操作的情况下,使用String的方法也有性能的提升:
    (3).由于String类型实现了Collection type的这样的接口,所以某些Swift的特性只有String类型能用,而NSString却没有;

    例如 for ... in 的枚举遍历所有字符:

    let words: String = "ILOVEYOU"
    for character in words{
          printf(charcter)
    }
    

    2.要使用NSString的情况

    (1).String类型有hasPrefix/hasSuffix方法来判断是否以某字符串开头或者结尾;

    if words.hasPrefix("ILOVE") {
                print("YES")
            }else{
                print("NO")
            }
    //以上代码输出结果为YES 
    

    NSString类型中虽然没有这种方法,但是NSString类型中却有containsString方法来判断字符串内部是否包含了某些字符,目前4.0版本containsString方法被缩写为:contains

     //String类型转换成NSString类型
    let nsWords = (words as NSString)
    print(nsWords)
    if nsWords.contains("ILOVE") {
          NSLog("YES")
    }else{
          NSLog("NO")
    }
    //输出结果YES
    

    (2). String类型截取字段的时候比较麻烦

    //NSString类型截取字段的方法(Swift4.0)
    let rangeStr1 = (words as NSString).substring(with: NSMakeRange(4, 2))
    
    //String类型截取字段的方法(现在这个方法在Swift4.0中已被取消)
    let index =  words.startIndex.advancedBy(4)
    let index2 = words.startIndex.advancedBy(6)
    let range = Range<String.Index>(start: index, end: index2)
    let rangeStr2 = words.substringWithRange(range) 
    

    字符串的字面量

    你可以在代码里使用一段预定义的字符串值作为字符串字面量。字符串字面量是由一对双引号包裹着的具有固定顺序的字符集。

    //字符串字面量可以用于为常量和变量提供初始值:
    let someString = "Some string literal value"
    //someString 是一个字符串常量,通过字符串字面量进行初始化,Swift语法系统会自动推断它为String类型
    

    多行字符串字面量的书写格式

    let quotation = """
    The White Rabbit put on his spectacles.  "Where shall I begin,
    please your Majesty?" he asked.
    "Begin at the beginning," the King said gravely, "and go on
    till you come to the end; then stop."
    """
    //注意:上面这段代码中,在两个 ' """ ' 中包含着一个多行字符串的字面量
    // 字符串中还可夹杂双引号,需要分行是直接用回车符号,非常好用.
    
    图解: 多行字符串.png

    字符串字面量的特殊字符

    • 转义字符\0(空字符)、\(反斜线)、\t(水平制表符)、\n(换行符)、\r(回车符)、"(双引号)、'(单引号)。
    • Unicode 标量,写成\u{n}(u为小写),其中n为任意一到八位十六进制数且可用的 Unicode 位码
    //示例代码
    let str1 = "\u{24}"
    print("str1:\(str1) str1")
    //输出结果 :str1:$ str1
    
    let str2 = "\0"
    print("str2:\(str2) str2")
    //输出结果 :str2: str2
    
    let str3 = "\\"
    print("str3:\(str3) str3")
    //输出结果:str3:\ str3
    
    let str4 = "\t"
    print("str4:\(str4) str4")
    //输出结果:str4:     str4
    
    let str5 = "\n"
    print("str5:\(str5) str5")
    //输出结果:str5:
     str5
    
    let str6 = "\n"
    print("str6:\(str6) str6")
    //输出结果: str6:
     str6
    
    let str7 = "\"abc\""
    print("str7:\(str7) str7")
    //输出结果:str7:"abc" str7
    
    let str8 = "\'abc\'"
    print("str9 \(str8) str9")
    //输出结果:str9 'abc' str9        
    

    字符与字符串

    您可通过for-in循环来遍历字符串,获取字符串中每一个字符的值:

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

    另外,通过标明一个Character类型并用字符字面量进行赋值,可以建立一个独立的字符常量或变量:

    let exclamationMark: Character = "!"
    

    字符串可以通过传递一个Character的数组作为自变量来初始化:

    let catCharacters: [Character] = ["C", "a", "t", "!", "🐱"]
    let catString = String(catCharacters)
    print(catString)
    // 打印输出:"Cat!🐱"
    

    String类型的拼接

    let hello = "Hello,"
    let world  = "World"
    let character: Character = "!"
    
    //过加法运算符(+)相加在一起
    var hellworld = hell + world // hellword 现在等于 "Hello,World"
    
    //可以用append()方法将一个字符附加到一个字符串变量的尾部
    hellworld.append(character) // hellworld 现在等于 "Hello,World!"
    
    //也可以通过加法赋值运算符 (+=) 将一个字符串添加到一个已经存在字符串变量上:
    let fuck = " fuck"
    hellworld += fuck //hellworld 现在等于 "Hello,World! fuck" 
    

    注意:
    您不能将一个字符串或者字符添加到一个已经存在的字符变量上,因为字符变量只能包含一个字符。这个有点像 Int.max 不能再加 1

    字符串插值

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

    获取字符串的字符数量

    //获取字符串的字符数量直接使用String自带的方法.count
    let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪"
    print("unusualMenagerie has \(unusualMenagerie.count) characters")
    // 打印输出 "unusualMenagerie has 40 characters"
    
    • 注意在 Swift 中,使用可拓展的字符群集作为Character值来连接或改变字符串时,并不一定会更改字符串的字符数量。
    var word = "cafe"
    print("the number of characters in \(word) is \(word.count)")
    // 打印输出 "the number of characters in cafe is 4"
    
    word += "\u{301}"    // 拼接一个重音, U+0301  得到 word = café
    
    print("the number of characters in \(word) is \(word.count)")
    // 打印输出 "the number of characters in café is 4"
    

    Unicode

    1). 什么是Unicode?

    Unicode是一个国际标准,用于文本的编码和表示。 它使您可以用标准格式表示来自任意语言几乎所有的字符,并能够对文本文件或网页这样的外部资源中的字符进行读写操作。 Swift 的String和Character类型是完全兼容 Unicode 标准的。

    2). Unicode的标量

    Unicode 标量是对应字符或者修饰符的唯一的21位数字:

    //例如U+0061表示小写的拉丁字母(LATIN SMALL LETTER A)("a")
    //U+1F425表示小鸡表情(FRONT-FACING BABY CHICK) ("🐥")
    let character = "\u{0061}" //  character 的值是 "a"
    let monkey = "\u{1f435}" // monkey 的值是 "🐵"
    
    • 注意: Unicode 码位(code poing) 的范围是U+0000到U+D7FF或者U+E000到U+10FFFF。Unicode 标量不包括 Unicode 代理项(surrogate pair) 码位,其码位范围是U+D800到U+DFFF。
    • 不是所有的21位 Unicode 标量都代表一个字符,因为有一些标量是留作未来分配的。
    可扩展的字形群集

    每一个 Swift 的Character类型代表一个可扩展的字形群。 一个可扩展的字形群是一个或多个 Unicode 标量的有序排列。

    //举例
    //字母é 可以是一个Unicode标量 "\u{00E9}",
    //也可以是一对标量:"\u{0065}\u{0301}"
    //在写代码时,前面的0都可以不谢
    let e1 = "\u{E9}" // e1 = é 
    let e2 = "\u{65}\u{301}" // e2 = é 
    //并且 e1.count == e2.count 
    
    //可扩展的字符群集是一个灵活的方法,用许多复杂的脚本字符表示单一的Character值。
    //来自朝鲜语字母表的韩语音节能表示为组合或分解的有序排列
    //在 Swift 都会表示为同一个单一的Character值:
    
    let precomposed: Character = "\u{D55C}"                  // 한
    let decomposed: Character = "\u{1112}\u{1161}\u{11AB}"   // ᄒ, ᅡ, ᆫ
    // precomposed 是 한, decomposed 是 한
    
    //可拓展的字符群集可以使包围记号(l例如:U+20DD)的标量包围其他 Unicode 标量:
    let enclosedEAcute: Character = "\u{E9}\u{20DD}"// enclosedEAcute 是 é⃝
    let myFirstName: Character = "庞\u{20DD}"// 日 是 庞⃝
    
    //地域性指示符号的 Unicode 标量可以组合成一个单一的Character值:
    let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
    // regionalIndicatorForUS 是 🇺🇸
    

    修改和访问String

    String 类型的:索引

    每一个String类型值都有一个关联的索引(index),String.Index,它对应着Sting中的每一个Chracter的位置。
    但是不同的Chracter可能占用着不同的内存空间,所以要想知道Chracter的确定位置,要从String的开头遍历每一个Unicode,因此String的索引不能用整数,例如:

    let str1: String = "\u{65}\u{301}" //等于 é
    print(a.startIndex)
    //ndex(_compoundOffset: 0, _cache: Swift.String.Index._Cache.utf16) 
    print(a.endIndex)
    //Index(_compoundOffset: 8, _cache: Swift.String.Index._Cache.utf16)
    

    使用startIndex属性可以获取一个String的第一个Character的索引。使用endIndex属性可以获取最后一个Character的后一个位置的索引。因此,endIndex属性不能作为一个字符串的有效下标。如果String是空串,startIndex和endIndex是相等的。
    通过调用 String 的 index(before:) 或 index(after:) 方法,可以立即得到前面或后面的一个索引。您还可以通过调用 index(_:offsetBy:) 方法来获取对应偏移量的索引,这种方式可以避免多次调用 index(before:) 或 index(after:) 方法。

    //定义一个String类型的常量str,这个由3个Chracter组成a / b / ć
    let str = "abc\u{301}" //abć
    //获取 str中的第一位Chracter
    let firstChracter =   str[str.startIndex] //注意这里使用的是中括号
    // 输出 a
    let secondChracter = str[str.index(after: str.startIndex)]
    //输出 b
    let thirdChracter = str[str.index(before:庞日富.endIndex)]
    //输出 c
    let index = str.index(str.startIndex, offsetBy: str.count - 1)
    let lastChracter = str[indx]
    //输出 ć
    

    视图获取越界的索引或者越界的索引值都会导致奔溃

    greeting[greeting.endIndex] // error
    greeting.index(after: endIndex) // error
    

    使用 indices 属性会创建一个包含全部索引的范围(Range),用来在一个字符串中访问单个字符。

    let abc = "abc" 
    for chracter in abc{
          print( chracter )
    }
    //输出:
    //a
    //b
    //c
    
    for index in abc.indices{
           print(abc[index])
    }
    //输出:
    //a
    //b
    //c
    
    插入和删除

    调用 insert(_:at:) 方法可以在一个字符串的指定索引插入一个字符,调用 insert(contentsOf:at:) 方法可以在一个字符串的指定索引插入一个段字符串。

    var welcome = "hello"
    welcome.insert("!", at: welcome.endIndex)
    // welcome 变量现在等于 "hello!"
    
    welcome.insert(contentsOf:" there", at: welcome.index(before: welcome.endIndex))
    // welcome 变量现在等于 "hello there!"
    

    调用 remove(at:) 方法可以在一个字符串的指定索引删除一个字符,调用 removeSubrange(_:) 方法可以在一个字符串的指定索引删除一个子字符串。

    welcome.remove(at: welcome.index(before: welcome.endIndex))
    // welcome 现在等于 "hello there"
    
    let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
    welcome.removeSubrange(range)
    // welcome 现在等于 "hello"
    

    注意: 您可以使用 insert(:at:)、insert(contentsOf:at:)、remove(at:) 和 removeSubrange(:) 方法在任意一个确认的并遵循 RangeReplaceableCollection 协议的类型里面,如上文所示是使用在 String 中,您也可以使用在 Array、Dictionary 和 Set 中。

    子字符串

    当你从字符串中获取一个子字符串 —— 例如,使用下标或者 prefix(_:) 之类的方法 —— 就可以得到一个 SubString 的实例,而非另外一个 String。Swift 里的 SubString 绝大部分函数都跟 String 一样,意味着你可以使用同样的方式去操作 SubString 和 String。然而,跟 String 不同的是,你只有在短时间内需要操作字符串时,才会使用 SubString。当你需要长时间保存结果时,就把 SubString 转化为 String 的实例:

    let greeting = "Hello, world!"
    let index = greeting.index(of: ",") ?? greeting.endIndex
    let beginning = greeting[..<index] //beginning在这里不是String类型,而是SubSquence
    // beginning 的值为 "Hello"
    
    // 把结果转化为 String 以便长期存储。
    let newString = String(beginning)
    

    String一样,每一个SubString都会在内存里保存字符集,StringSubString的区别在于性能优化上,SubString可以重用原String的内存空间,或者另一个SubString的内存空间,(String也有同样的优化,但是如果两个String使用同样的内存的话,他们就会相等),着意味着子修改StringSubString之前都不需要消耗性能去赋值内存,SubString不适合长期储存,因为它重用了Stirng的内存空间,元String的内存空间必须保留,知道它的SubString不再被使用。

    //案例
    let greeting:String = "Hello,World!"
    //意味着它在内存里有一片空间保存字符集
    let index = greeting.index(of: ",") ?? greeting.endIndex
    let beginning = greeting[..<index]
    //beginning 是 greeting 的 SubString ,所以它重用了greenting的内存
    let newString = String(beginning) 
    //newString 是使用 beginning (beginning是一个SubString)创建的,它拥有自己的内存,
    

    greeting beginning newString的关系可以用下图解释:

    stringSubstring.png

    比较字符串

    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")
    }
    // 打印输出 "These two strings are considered equal"
    
    //如果两个字符串 print出来的值是相等的,我们就认为他是相等的
    let strA = "\u{E9}" // é
    let strB = "\u{65}\u{301}" //  é
    
    if strA == strB {
        print( " strA 和 strB 是相等的" ) //代码会执行到这里来
    }
    

    注意:
    在 Swift 中,字符串和字符并不区分地域(not locale-sensitive)。

    前缀/后缀相等

    通过调用字符串的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"
    ]
    
    //统计 Act1 前缀出现的次数
    var act1SceneCount = 0 
    for scene in romeoAndJuliet {
        if scene.hasPrefix("Act 1 ") {
            act1SceneCount += 1
        }
    }
    print("There are \(act1SceneCount) scenes in Act 1")
    // 打印输出 "There are 5 scenes in Act 1"
    
    //统计后缀:Capulet's mansion 和后缀:Friar Lawrence's cell分别出现的次数
    var mansionCount = 0
    var cellCount = 0
    for scene in romeoAndJuliet {
        if scene.hasSuffix("Capulet's mansion") {
            mansionCount += 1
        } else if scene.hasSuffix("Friar Lawrence's cell") {
            cellCount += 1
        }
    }
    print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
    // 打印输出 "6 mansion scenes; 2 cell scenes"
    
    

    相关文章

      网友评论

        本文标题:Swift 中 NSString 与 String 的认识

        本文链接:https://www.haomeiwen.com/subject/qlshfftx.html