计算 Swift 字符串中的行数
Count the number of lines in a Swift String
从网络服务读取了一个中等大小的文件(大约 500kByte)后,我得到了一个最初编码为 .isolatin1
的常规 Swift 字符串 (lines
)。在实际拆分之前,我想(快速)计算行数以便能够初始化进度条。
最好的 Swift 成语是什么?
我想到了以下内容:
let linesCount = lines.reduce(into: 0) { (count, letter) in
if letter == "\r\n" {
count += 1
}
}
这看起来还不错,但我在问自己是否有 shorter/faster 方法来做到这一点。 characters
属性 提供对一系列 Unicode 字符的访问,这些字符将 \r\n
视为唯一的实体。用所有 CharacterSet.newlines
检查这个是行不通的,因为 CharacterSet
不是一组 Character
而是一组 Unicode.Scalar
(在我的书中有点违反直觉)这是一组 代码点(其中 \r\n 算作两个代码点),而不是 字形。正在尝试
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
}
}
将计数为 6 而不是 3。所以这比上面的方法更通用,但是对于 CRLF 行结尾它不能正确工作。
有没有办法允许更多的行结束约定(如 CharacterSet.newlines
中),同时仍能为 CRLF 获得正确的结果?可以用更少的代码计算行数(同时仍然保持可读性)吗?
如果您可以在 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)
}
考虑到所有换行类型,例如“\r\n”、“\n”等,它正确地分隔了行:
Hello, playground
here too
Galahad
在我的示例中,我打印了这些行,但是根据您的需要对它们进行计数很简单 - 我的版本仅用于演示。
因为我没有找到计算换行符的通用方法,所以我最终只是通过使用
遍历所有字符来解决我的问题
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% 多一点。我不知道 Swift 工程师在他们的袖子里藏了什么把戏,但他们确实很快 enumerateLines
了。这只是为了记录。
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
您可以使用以下扩展程序
extension String {
var numberOfLines: Int {
return self.components(separatedBy: "\n").count
}
}
我使用这个,Apple 提供的 CharacterSet
,专为这个任务制作:
let newLines = text.components(separatedBy: .newlines).count - 1
从网络服务读取了一个中等大小的文件(大约 500kByte)后,我得到了一个最初编码为 .isolatin1
的常规 Swift 字符串 (lines
)。在实际拆分之前,我想(快速)计算行数以便能够初始化进度条。
最好的 Swift 成语是什么?
我想到了以下内容:
let linesCount = lines.reduce(into: 0) { (count, letter) in
if letter == "\r\n" {
count += 1
}
}
这看起来还不错,但我在问自己是否有 shorter/faster 方法来做到这一点。 characters
属性 提供对一系列 Unicode 字符的访问,这些字符将 \r\n
视为唯一的实体。用所有 CharacterSet.newlines
检查这个是行不通的,因为 CharacterSet
不是一组 Character
而是一组 Unicode.Scalar
(在我的书中有点违反直觉)这是一组 代码点(其中 \r\n 算作两个代码点),而不是 字形。正在尝试
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
}
}
将计数为 6 而不是 3。所以这比上面的方法更通用,但是对于 CRLF 行结尾它不能正确工作。
有没有办法允许更多的行结束约定(如 CharacterSet.newlines
中),同时仍能为 CRLF 获得正确的结果?可以用更少的代码计算行数(同时仍然保持可读性)吗?
如果您可以在 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)
}
考虑到所有换行类型,例如“\r\n”、“\n”等,它正确地分隔了行:
Hello, playground
here too
Galahad
在我的示例中,我打印了这些行,但是根据您的需要对它们进行计数很简单 - 我的版本仅用于演示。
因为我没有找到计算换行符的通用方法,所以我最终只是通过使用
遍历所有字符来解决我的问题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% 多一点。我不知道 Swift 工程师在他们的袖子里藏了什么把戏,但他们确实很快 enumerateLines
了。这只是为了记录。
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
您可以使用以下扩展程序
extension String {
var numberOfLines: Int {
return self.components(separatedBy: "\n").count
}
}
我使用这个,Apple 提供的 CharacterSet
,专为这个任务制作:
let newLines = text.components(separatedBy: .newlines).count - 1