V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
iOS 开发实用技术导航
NSHipster 中文版
http://nshipster.cn/
cocos2d 开源 2D 游戏引擎
http://www.cocos2d-iphone.org/
CocoaPods
http://cocoapods.org/
Google Analytics for Mobile 统计解决方案
http://code.google.com/mobile/analytics/
WWDC
https://developer.apple.com/wwdc/
Design Guides and Resources
https://developer.apple.com/design/
Transcripts of WWDC sessions
http://asciiwwdc.com
Cocoa with Love
http://cocoawithlove.com/
Cocoa Dev Central
http://cocoadevcentral.com/
NSHipster
http://nshipster.com/
Style Guides
Google Objective-C Style Guide
NYTimes Objective-C Style Guide
Useful Tools and Services
Charles Web Debugging Proxy
Smore
banxi1988
V2EX  ›  iDev

Swift 中两个让人难受的字符串操作方式及正确姿势

  •  
  •   banxi1988 ·
    banxi1988 · 2015-06-25 09:02:57 +08:00 · 5228 次点击
    这是一个创建于 3475 天前的主题,其中的信息可能已经有所发展或是发生改变。
    1. 其一是 获得字符数的属性。
    在 在我最开始使用 Swift 编程时,我惊奇的发现 Swift的 String 结构没有获得字符串长度的 `size`,`length`,`count` 之类的属性或者方法。 然后发现可以使用 `countElements` 这样一个全局方法。
    然后在 Swift 1.2 中这个方法名称简化成 `count` 。
    虽然说也有其他编程语言如 Python 是用一个全局的内置函数 `len` 来求字符数 (1)的。
    但是作为习惯了 `NSString` 来说,这有一点点难受。 所以一般会在 `String` 的 extension
    中加上 `count` 或者 `length` 这么一个属性。

    但是从 Swift 2.0 开始 对于 `String` 结构来说,`count` 方法已经不能用了。
    经过我的探索,我发现,正确的获得字符串方法。变成了如下方法:

    ```Swift
    let hello = "Hello,world"
    hello.characters.count // 11
    ```

    当然如果你误用了,Swift 提供的另一个以 `length` 开头的 `lengthOfBytesUsingEncoding` 方法,就可能有麻烦了。因为 它确实是统计字符所使用字节数的 :

    ```Swift
    let hello = "Hello,world"
    hello.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) // 11
    let hello2 = "Hello,世界"
    hello2.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) // 14
    ```

    2. Swift 中另一个常用操作变得不很不直观的地方就是,substring 类的操作了。
    一开始,或者从 `NSString` 时代的操作习惯,获得一个字符串的前三个字符,可能这么写:

    ```Swift
    let hello = "Hello,world"
    let hel = hello.substringToIndex(3)
    ```
    或者想着 Swift 字符串可能支持切片,这样写: `hello[0..3]`

    但是这些都会报编译错误。

    之前我是将其转成 `NSString` 再操作的。 后来在 WWDC 上发现了下面的 Swift Style 的写法:

    ```Swift
    let hello = "Hello,world"
    let hel = hello.substringToIndex(advance(hello.startIndex, 3)) // "Hel"
    ```

    核心在于这里的 Index 不是整形的,而是一个 `String.Index` 结构。
    `String` 结构本身提供了 `startIndex` 和 `endIndex`
    然后想得到其他位置的 Index 你一般就需要使用 这个全局的 `advance` 方法。

    当然如果对 `Index` 结构多做些研究,根据它提供的 `successor` 方法,上面任务可以使用如下代码完成:

    ```Swift
    let hello = "Hello,world"
    let hel2 = hello.substringToIndex(hello.startIndex.successor().successor().successor()) // "Hel"
    ```


    值得注意的是,为了防止越界,如果是取得最多18个字符,可以这样改写:

    ```Swift
    let hello = "Hello,world"
    let hel = hello.substringToIndex(advance(hello.startIndex, 18,hello.endIndex)) // "Hello,world"
    ```
    12 条回复    2015-06-26 07:30:26 +08:00
    babyname
        1
    babyname  
       2015-06-25 09:07:33 +08:00
    这么反人类?
    onevcat
        2
    onevcat  
       2015-06-25 09:15:02 +08:00
    可以去看看今年的 Session 408 以及标准库里 String 的实现方式你就会知道为什么要这么做了。尽量不要用传统 OO 的思维来看 Swift,这样会写得舒心一些。
    pheyer
        3
    pheyer  
       2015-06-25 09:15:19 +08:00
    奇怪为什么不用length呢
    BuilderQiu
        4
    BuilderQiu  
       2015-06-25 09:17:29 +08:00
    有点诡异
    反人类+1
    forkon
        5
    forkon  
       2015-06-25 09:20:18 +08:00
    swift变得强大的同时确实把一些东西整复杂了,当然更多的是简便
    PrideChung
        6
    PrideChung  
       2015-06-25 09:25:37 +08:00
    字符串切片的操作的确是很让人犯晕,在Ruby,Python里面都是很简单直观的写法,到Swift里面我是写一次忘一次
    lilydjwg
        7
    lilydjwg  
       2015-06-25 10:26:31 +08:00
    Rust 里取字符数也是类似的,s.chars().count()。取(UTF-8)字节数是 s.bytes().count()。还有个 s.graphemes().count() 可以用,它是数 grapheme cluster 数的,比如 ç 是两个字符,却是一个 grapheme cluster(然后还有 ç 是一个字符)。人类的文字很复杂的,Unicode 的某些设计使之更复杂了……
    fwee
        8
    fwee  
       2015-06-25 10:48:50 +08:00
    @lilydjwg 那是1.8,1.9早就直接size取字符
    otakustay
        9
    otakustay  
       2015-06-25 11:03:55 +08:00
    看上去很像是ObjC继承过来的理念,让代码本身的可读性接近自然语言,是一种OO与其它编程模式的混合体
    chmlai
        10
    chmlai  
       2015-06-25 11:12:57 +08:00   ❤️ 1
    因为 Unicode, 早超过16位了, NSString.length 返回的长度也是不可靠的, 算得是内部 UTF-16 码元的数量, 不是字符数.
    ibremn
        11
    ibremn  
       2015-06-25 12:21:06 +08:00   ❤️ 1
    最初的 FoundationKit 是随着 Unicode 标准一起诞生的,那时 Unicode 只有16位。后来 Unicode 扩展了,但 FoundationKit 的设计仍旧被保留下来了,所以 NSString.length 取到的东西,实际上是 UTF-16 码元数量,并非实际 charactor 数量,由此也带来了大量的令人困惑的 API 和很多错误的用法。看一下这个文章吧: http://objccn.io/issue-9-1/

    新语言了,厘清一下这些概念也许会好些。我觉得以后应该也会有一些更简单些的写法,但应该不会再和 NSString 一样了。。
    banxi1988
        12
    banxi1988  
    OP
       2015-06-26 07:30:26 +08:00
    @onevcat 408 是讲 Protocol Oriented Programming 的,我发我现我之前看过,感觉很深一点是,Swift 的 Protocol 表现力增强不少。Apple 的这些 开发者 讲得很好,有设计的虚拟人物,循序渐进。

    但是说到 String 类中字符个数的功能,Apple 不提供, 大家也还是会在扩展中 弄一个 `count`, `length` 之类的扩展的。 因为这是一种很强的使用习惯了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2549 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 10:43 · PVG 18:43 · LAX 02:43 · JFK 05:43
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.