【问题标题】:Swift 4 String index offset by too slow while processing a large string处理大字符串时Swift 4字符串索引偏移太慢
【发布时间】:2018-04-30 10:05:41
【问题描述】:

我正在处理一个 csv 文件,其中最后一列的格式并不总是相同。 每一行都有这样的结构:

"Root/Word1","some string","some string","some œ0'fqw[唃#”≠§
\nfw@\tfa0j
"
"Root/Word2","some string","some string","some string"
...

所以 6 列,最后一个可以包含 \n。这使得很难按组件拆分。 另一个限制是所有字符串都可以是任何可能的特殊字符。这使得使用正则表达式变得困难。

我决定先用蛮力解决问题。 (是的,我已经看到索引偏移量为 O(n)。但无法提出替代方案。)

static func importData(_ db: DB) {
    let csvString = readDataFromCSV(fileName: "data", fileType: "csv")!

    let totalCharCount = csvString.count
    print("total: \(totalCharCount)")
    for i in 0..<totalCharCount {
        print(i)
        if i+5 >= totalCharCount {
            continue
        }
        let index = csvString.index(csvString.startIndex, offsetBy: i)
        let endIndex = csvString.index(csvString.startIndex, offsetBy:i+5)

        let part = csvString[index ..< endIndex]
        if part == "Root/" {
            let accum = lookInside(i: i, totalCharCount: totalCharCount, csvString: csvString)

            var rows = accum.components(separatedBy: "\",\"")

            if var lastt = rows.last {
                lastt.removeLast()
                lastt.removeLast()
                rows[rows.count-1] = lastt
            }
        }
    }
}

static func lookInside(i:Int, totalCharCount: Int, csvString: String) -> String {
    var accum = ""
    var found = false
    var j = i+5
    while !found {

        if j+5 >= totalCharCount {
            found = true
        }
        let index2 = csvString.index(csvString.startIndex, offsetBy: j)
        let endIndex2 = csvString.index(csvString.startIndex, offsetBy:j+5)

        if csvString[index2 ..< endIndex2] == "Root/" {
            found = true
            accum.removeLast()
        } else {
            accum += String(csvString[index2])
        }
        j += 1
    }

    return accum
}

基本上,我正在遍历整个字符串以寻找模式“Root/”。 找到后,我从这一刻前进到模式的下一次出现。

问题是 csv 会生成一个 200k 字符长的字符串,当我在模拟器上运行它时,它会持续很长时间(约 30 分钟)。

所以现在我在这里寻求帮助,因为根据 Instruments,所有时间都消耗在 String.index(offset by) 方法中,该方法被调用了太多次。

【问题讨论】:

  • 明显的微优化是只从前一个索引开始索引,而不是每次都从字符串的开头开始。但是,您是否有理由不使用 OTS CSV 解析器?这个临时的似乎在某些情况下会失败。
  • 谢谢。好点子。将 thry 你提到的图书馆
  • @Ssswift 所说的不仅仅是微优化。遍历一个字符串是O(n),由于你每次都是从csvString.startIndex开始,它很快就变成了O(n^2)操作。由于您的问题是关于性能优化,请将完整的 CSV 文件上传到 Pastebin 并在此处包含一个链接
  • 你说有 6 列,但我只数 4 列。您能否确认这些列是否可能包含"?引用双引号(即\")是否合法?还是每条记录准确有 8 个(12 个?)双引号,并且记录的结尾是双引号,这是真的吗?删除Root/ 是否重要,或者只是您查找记录的方式?

标签: swift string algorithm performance indexing


【解决方案1】:

首先,如果您需要读取整个数据集,我强烈建议您使用 csv 解析器,例如https://github.com/naoty/SwiftCSV

其次,如果您要在整个文件中查找特定条目,您可能希望使用range 函数:Answer using String.range

【讨论】:

  • 哇,不知道。刚去了临时的。这可能会解决我的问题,但我仍然很好奇如何解决这个遍历问题。不,我不是在寻找特定的条目。只需要重新格式化输入的 csv。
  • 由于这只是几个链接,您真的应该将其发布为评论,而不是答案。
  • rmaddy:第一个是通用解决方案——“使用 CSV 解析器”——链接只是可以使用的一个示例。我认为这是一个很好的答案。
【解决方案2】:

Shlemiel 找到了一份街头画家的工作,负责画虚线 在路中间。第一天他拿了一罐油漆 走到路边,完成 300 码的路。 “真漂亮 好的!”他的老板说,“你是一个快速的工人!”并付给他一戈比。

第二天,Shlemiel 只完成了 150 码。 “嗯,那不是 几乎和昨天一样好,但你仍然是一个快速的工人。 150码 是可敬的,”并付给他一个戈比。

第二天,Shlemiel 粉刷了 30 码的道路。 “只有30个!”喊叫 他的老板。 “这是不能接受的!第一天你做了十次 这么多工作!怎么回事?”

“我情不自禁,”Shlemiel 说。 “我每天都越来越远 远离油漆罐!”

引用Joel on Software

您需要了解字符串的工作原理。 2001 年 Joel Spolsky 撰写上述故事时,字符串处理很慢。通过正确的 Unicode 处理,事情变得更加昂贵。

您并没有真正遍历字符串。您使用String.startIndex 一遍又一遍地重新开始。

改为使用返回String.Index 结果的函数。这是对字符串的有效索引。用它来保存你的最后一个位置,然后相对于这个位置工作。如果您在开始时只使用一次String.Index,您的代码会更快。

【讨论】:

  • 那些函数是什么?
  • 其中一个是String.index(:offsetBy:),你已经使用了,但是没有好好使用。例如。将结果传递给 lookInside 而不是第一个 Int 参数。然后相对于该位置工作。
【解决方案3】:

我找到了一个仅使用组件和匹配模式的解决方案。 这可以在不到 4 秒的时间内完成。 感谢您提供的所有信息,我从中学到了很多。

        let csvString = readDataFromCSV(fileName: "data", fileType: "csv")!
        var components = csvString.components(separatedBy: "Root/")
        var result = [Note]()
        for i in components {
            let noteComponents = i.components(separatedBy: "\",\"")

【讨论】:

    猜你喜欢
    • 2023-04-04
    • 2018-12-28
    • 1970-01-01
    • 1970-01-01
    • 2019-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多