【问题标题】:How to split a string by new lines in Swift如何在 Swift 中用换行符分割字符串
【发布时间】:2015-11-08 09:32:19
【问题描述】:

我有一个从文本文件中得到的字符串。

文本文件:

Line 1
Line 2
Line 3
...

我想把它转换成一个数组,每行一个数组元素。

[ "Line 1", "Line 2", "Line 3", ... ]

根据文件的保存方式,字符串可以采用以下形式之一:

  • string = "Line 1\nLine 2\nLine 3\n..." 其中\n 是换行符(换行符)

  • string = "Line 1\r\nLine 2\r\nLine 3\r\n..." 其中\r 是回车符。

据我了解,\n 目前普遍用于 Apple/Linux,而\r\n 用于 Windows。

如何在任何换行符处拆分字符串以获得没有任何空元素的字符串数组?

更新

下面有几种解决方案。在这一点上,我没有任何令人信服的理由来选择一个比其他的更正确。可能影响选择的一些因素可能是(1)它有多“Swift”和(2)它对于很长的字符串有多快。您可以通过点赞其中一个或多个和/或发表评论来提供反馈。

See my summarized answer here

【问题讨论】:

  • 大概就像split(stringFromFile, { newLineChars.characterIsMember($0) }, maxSplit: Int.max, allowEmptySlices: false)一样简单
  • @njzk2,我喜欢它的外观,但我收到一个错误“无法使用String, (unichar) -> Bool, maxSplit Int, allowEmptySlices: Bool) 类型的参数列表调用split”可能是Swift 2 中的语法发生了变化?
  • 让 test1 = "Line1\n\rLine2\n\rLine3\n\rLine4\nLine5\r\r\n\rLine6" let t1 =test1.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet()) 让t2 = t1.filter{ $0 != ""} 对我有用(Xcode 7,beta5)

标签: arrays string swift newline


【解决方案1】:

Swift 5.2 或更高版本

您可以使用新的Character 属性isNewline 拆分您的String

let sentence = "Line 1\nLine 2\nLine 3\n"
let lines = sentence.split(whereSeparator: \.isNewline)
print(lines)   // "["Line 1", "Line 2", "Line 3"]\n"

您还可以扩展 StringProtocol 并创建一个 lines 实例属性来将字符串行分解为子序列:

extension StringProtocol {
    var lines: [SubSequence] { split(whereSeparator: \.isNewline) }
}

let sentence = "Line 1\nLine 2\r\nLine 3\n"
for line in sentence.lines {
    print(line)
}
let lines = sentence.lines  // ["Line 1", "Line 2", "Line 3"]


原答案

你可以使用String方法enumerateLines

枚举字符串中的所有行。

Swift 3 或更高版本

let sentence = "Line 1\nLine 2\nLine 3\n"
var lines: [String] = []
sentence.enumerateLines { line, _ in
    lines.append(line)
}
print(lines)   // "["Line 1", "Line 2", "Line 3"]\n"

extension String {
    var lines: [String] {
        var result: [String] = []
        enumerateLines { line, _ in result.append(line) }
        return result
    }
}

let sentence2 = "Line 4\nLine 5\nLine 6\n"
let sentence2Lines = sentence2.lines
print(sentence2Lines)    // "["Line 4", "Line 5", "Line 6"]\n"
let sentence3 = "Line 7\r\nLine 8\r\nLine 9\r\n"
let sentence3Lines = sentence3.lines
print(sentence3Lines)  // "["Line 7", "Line 8", "Line 9"]\n"

【讨论】:

【解决方案2】:
     let getName = "Davender+Verma"
     let cleanFile = getName.replacingOccurrences(of: "+", with: "+\n")
     self.upcomingViewPetName.text = cleanFile


     Output: Davender+
            verma

Or 
     let getName = "Davender+Verma"
     let cleanFile = getName.replacingOccurrences(of: "+", with: "\n")
     self.upcomingViewPetName.text = cleanFile

Output:     Davender
            verma

【讨论】:

    【解决方案3】:

    郑重声明,Swift 的 Foundation CharacterSet 可以在 split 中使用:

    备选方案 1

    extension String {
        var lines: [String] {
            return split { String($0).rangeOfCharacter(from: .newlines) != nil }.map(String.init)
        }
    }
    

    备选方案 2

    extension String {
        var lines: [String] {
            return split { CharacterSet.newlines.contains($0.unicodeScalars.first!) }.map(String.init)
        }
    }
    

    【讨论】:

      【解决方案4】:

      斯威夫特 4:

      如果您还没有这样做,我建议您先将 CSV 保存到字符串中,然后通过删除不必要的回车来“清理”字符串

              let dataString = String(data: yourData!, encoding: .utf8)!
      
              var cleanFile = dataString.replacingOccurrences(of: "\r", with: "\n")
              cleanFile = cleanFile.replacingOccurrences(of: "\n\n", with: "\n")
      

      上面会给你一个最理想格式的字符串,然后你可以使用 \n 作为分隔符来分隔字符串:

              let csvStrings = cleanFile.components(separatedBy: ["\n"])
      

      现在您有一个包含 3 个项目的数组,例如:

      ["Line1","Line2","Line3"]

      我正在使用 CSV 文件,在执行此操作后,我将项目拆分为组件,所以如果您的项目类似于:

      ["Line1,Line2,Line3","LineA,LineB,LineC"]

              let component0 = csvStrings[0].components(separatedBy: [","]) // ["Line1","Line2","Line3"]
              let component1 = csvStrings[1].components(separatedBy: [","]) // ["LineA","LineB","LineC"]
      

      【讨论】:

        【解决方案5】:

        Xcode 8.2、Swift 3.0.1 中:

        使用 NSString 方法 components(separatedBy:)

        let text = "line1\nline2"
        let array = text.components(separatedBy: CharacterSet.newlines)
        

        或者使用String方法enumerateLines,比如Leo Dabus的回答

        【讨论】:

        • let array = text.components(separatedBy: .newlines)
        • 虽然let array = text.components(separatedBy: .newlines) 看起来干净而迅速,但它实际上拆分了 CRLF 行终止符 (\r\n)TWICE,导致出现空行。
        • 有什么方法可以将组件(separetdBy:) 与 CRLF 行终止符一起使用?
        • @Raisen 您只需要在分隔新行后过滤空字符串。您还可以使用我上面建议的 split 方法,并在需要时将生成的子字符串映射到字符串。
        【解决方案6】:

        这个答案是对已经给出的其他解决方案的总结。它来自我的fuller answer,但在这里提供实际的方法选择会很有用。

        换行通常使用\n 字符,但也可以使用\r\n(来自保存在Windows 中的文件)。

        解决方案

        1. componentsSeparatedByCharactersInSet

        let multiLineString = "Line 1\nLine 2\r\nLine 3\n"
        let newlineChars = NSCharacterSet.newlineCharacterSet()
        let lineArray = multiLineString.componentsSeparatedByCharactersInSet(newlineChars).filter{!$0.isEmpty}
        // "[Line 1, Line 2, Line 3]"
        

        如果未使用filter,则\r\n 将生成一个空数组元素,因为它被计为两个字符,因此在同一位置将字符串分隔两次。

        2。 split

        let multiLineString = "Line 1\nLine 2\r\nLine 3\n"
        let newlineChars = NSCharacterSet.newlineCharacterSet()
        let lineArray = multiLineString.utf16.split { newlineChars.characterIsMember($0) }.flatMap(String.init)
        // "[Line 1, Line 2, Line 3]"
        

        let multiLineString = "Line 1\nLine 2\r\nLine 3\n"
        let lineArray = multiLineString.characters.split { $0 == "\n" || $0 == "\r\n" }.map(String.init)
        // "[Line 1, Line 2, Line 3]"
        

        这里 \r\n 被计为单个 Swift 字符(扩展字素簇)

        3. enumerateLines

        let multiLineString = "Line 1\nLine 2\r\nLine 3\n"
        var lineArray = [String]()
        multiLineString.enumerateLines { (line, stop) -> () in
            lineArray.append(line)
        }
        // "[Line 1, Line 2, Line 3]"
        

        有关enumerateLine 语法的更多信息,另请参阅this answer

        注意事项:

        • 多行字符串通常不会同时混合\r\n\n,但我在这里这样做是为了表明这些方法可以处理这两种格式。
        • NSCharacterSet.newlineCharacterSet() 是定义为 (U+000A–U+000D, U+0085) 的换行符,包括 \r\n
        • 此答案是对my previous question 的答案的摘要。阅读这些答案以了解更多详情。

        【讨论】:

        • 我添加了一种不同的方法,以防您想包含它。
        • @Cœur,很高兴知道。有时间我会仔细研究的。
        【解决方案7】:

        在 Swift 2 中,顶级 split 函数现在是 CollectionType 上的一个方法(Strings 中的每个“字符视图”都符合该方法)。该方法有两个版本,您希望一个将闭包作为谓词来指示是否应将给定元素视为分隔符。

        您可以使用string.utf16 将字符串中的字符集合作为UTF16 字符集合获取,从而使它们与NSCharacterSet API 兼容。这样,我们可以轻松地在闭包内部检查字符串中的给定字符是否是换行符集的成员。

        值得注意的是split(_:) 将返回一个SubSequence 字符(基本上是一个Slice),因此它需要转换回通常更有用的字符串数组。我在下面使用flatMap(String.init) 完成了此操作-String 上的UTF16View 初始化程序是失败的,因此使用flatMap 将忽略任何可能返回的nil 值,确保您获得一组非可选字符串返回。

        因此,对于一个很好的类似 Swift 的方式来执行此操作:

        let str = "Line 1\nLine 2\r\nLine 3\n"
        let newlineChars = NSCharacterSet.newlineCharacterSet()
        let lines = str.utf16.split { newlineChars.characterIsMember($0) }.flatMap(String.init)
        // lines = ["Line 1", "Line 2", "Line 3"]
        

        这很好的是split 方法有一个参数allowEmptySubsequences,它确保您不会在结果中收到任何空字符序列。默认情况下这是false,因此您实际上根本不需要指定它。

        编辑

        如果您想完全避免 NSCharacterSet,您可以轻松拆分符合 unicode 的 Characters 集合。

        let lines = str.characters.split { $0 == "\n" || $0 == "\r\n" }.map(String.init)
        

        Swift 能够将"\r\n" 视为单个扩展字素簇,将其用作单个Character 进行比较,而不是创建String。另请注意,从Character 创建字符串的初始化程序是不可失败的,因此我们可以只使用map

        【讨论】:

        • 我觉得你可以用map(String.init)代替map { String($0)! }
        • @hennes 你可以,虽然因为String 上的UTF16View 初始化程序是失败的,它会返回和可选字符串数组([String?]),我不认为你可以直接强制解开。最好使用flatMap(String.init),它会自动忽略nils 并返回一个非可选数组。但是感谢您的提示,我已经更新了答案。
        • 对于包含表情符号或其他需要在 utf16 中进行代理编码的字符的文本意味着什么?我现在也需要担心大小结尾编码吗?是否有一种“纯 Swift”方式在不使用 NSCharacterSet 的情况下打破换行符?
        • 据我所知(并且据我实验),这应该无关紧要。 utf16 确实将字符串分解为那些代码单元——拆分扩展的字素簇——但在删除换行符之后,切片无论如何都会重新组合成 unicode 字符串。避免NSCharacterSet 并使用符合unicode 的Character 集合是完全可以的——我已经用一个例子编辑了答案。
        • 我很难为这个分裂问题找到一个好的答案,但你编辑的代码终于为我解决了问题。所有使用 NSCharacterSet 的解决方案都不适用于 IBM Sandbox。
        【解决方案8】:

        如何在任何换行符处拆分字符串以获得没有任何空元素的字符串数组?

        你快到了 - 只是尾随闭包在这里有所不同:

        let array = stringFromFile.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet()).filter{!$0.isEmpty}
        

        等同于:

        let newLineChars = NSCharacterSet.newlineCharacterSet() // newline characters defined as (U+000A–U+000D, U+0085)
        let array = stringFromFile.componentsSeparatedByCharactersInSet(newLineChars).filter{!$0.isEmpty}
        

        ETA:在尾随关闭时删除了不必要的额外括号

        【讨论】:

          【解决方案9】:
          let test1 = "Line1\n\rLine2\nLine3\rLine4"
          let t1 = test1.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet())
          let t2 = t1.filter{ $0 != "" }
          let t3 = t1.filter{ !$0.isEmpty }
          

          【讨论】:

            猜你喜欢
            • 2013-02-15
            • 1970-01-01
            • 2022-07-06
            • 2014-03-20
            • 2013-11-15
            • 1970-01-01
            • 1970-01-01
            • 2021-01-10
            • 2012-07-07
            相关资源
            最近更新 更多