计算Swift字符串中的行数

时间:2017-09-29 14:25:01

标签: swift unicode line-endings

从Web服务中读取中等大小的文件(大约500kByte)后,我有一个最初用.isolatin1编码的常规Swift字符串(let linesCount = lines.reduce(into: 0) { (count, letter) in if letter == "\r\n" { count += 1 } } )。在实际拆分之前,我想计算行数(快速),以便能够初始化进度条。

实现这一目标的最好的Swift成语是什么?

我想出了以下内容:

characters

这看起来并不太糟糕,但我问自己是否有更短/更快的方法。 \r\n属性提供对一系列Unicode字形的访问,这些字形将CharacterSet.newlines视为仅一个实体。使用所有CharacterSet进行检查不起作用,因为Character不是一组Unicode.Scalar,而是一组var lines = "Hello, playground\r\nhere too\r\nGalahad\r\n" lines.unicodeScalars.reduce(into: 0) { (cnt, letter) in if CharacterSet.newlines.contains(letter) { cnt += 1 } (在我的书中有点反直觉),这是一组代码点(其中\ r \ n计为两个代码点),而不是 graphemes 。试图

CharacterSet.newlines

}

将计为6而不是3.所以这比上面的方法更通用,但它对CRLF行结尾不能正常工作。

有没有办法允许更多的行结束约定(如在{{1}}中)仍能达到CRLF的正确结果?可以用更少的代码计算行数(同时仍然保持可读性)?

4 个答案:

答案 0 :(得分:7)

如果您可以在NSString上使用Foundation方法,我建议使用

enumerateLines(_ block: @escaping (String, UnsafeMutablePointer<ObjCBool>) -> Void)

以下是一个例子:

import Foundation

let base = "Hello, playground\r\nhere too\r\nGalahad\r\n"
let ns = base as NSString

ns.enumerateLines { (str, _) in
    print(str)
}

它会正确地分隔线条,同时考虑所有换行类型,例如&#34; \ r \ n&#34;,&#34; \ n&#34;等等:

  

你好,游乐场
  这里也是   加拉哈德

在我的例子中,我打印了这些行,但是根据需要计算它们是微不足道的 - 我的版本仅用于演示。

答案 1 :(得分:5)

由于我没有找到计算换行符的通用方法,我最终只是通过使用

迭代所有字符来解决我的问题
let linesCount = text.reduce(into: 0) { (count, letter) in
     if letter == "\r\n" {      // This treats CRLF as one "letter", contrary to UnicodeScalars
        count += 1
     }
}

我确信这比计算行计数要快得多,但我决定最终进行测量。今天我终于到了,发现......我不能再错了。

10000行字符串在大约1.0秒内计算如上所述的行,但是使用

计算枚举
var enumCount = 0
text.enumerateLines { (str, _) in
    enumCount += 1
}

只用了大约0.8秒,并且持续快了20%多一点。我不知道斯威夫特的工程师躲在他们的衣服里有什么技巧,但他们确实能很快地设法enumerateLines。这只是为了记录。

答案 2 :(得分:1)

您可以使用以下扩展程序

extension String {

    var numberOfLines: Int {
        return self.components(separatedBy: "\n").count
    }

}

答案 3 :(得分:0)

Swift 5 扩展

extension String {
    
    func numberOfLines() -> Int {
        return self.numberOfOccurrencesOf(string: "\n") + 1
    }

    func numberOfOccurrencesOf(string: String) -> Int {
        return self.components(separatedBy:string).count - 1
    }
}

示例:

let testString = "First line\nSecond line\nThird line"
let numberOfLines = testString.numberOfLines() // returns 3