如何在Swift中用新行拆分字符串

时间:2015-08-15 05:03:41

标签: arrays string swift newline

我有一个从文本文件中获取的字符串。

文字档案:

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。

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

更新

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

See my summarized answer here

9 个答案:

答案 0 :(得分:47)

Swift 5或更高版本

您可以使用新的String媒体资源isNewline拆分Character

let sentence = "Line 1\nLine 2\nLine 3\n"
var lines = sentence.split { $0.isNewline }
print(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]"
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"]


let sentence3 = "Line 7\r\nLine 8\r\nLine 9\r\n"
let sentence3Lines = sentence3.lines

print(sentence3Lines)  // "[Line 7, Line 8, Line 9]"

答案 1 :(得分:18)

Xcode 8.2,Swift 3.0.1:

使用NSString方法组件(sortedBy:)

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

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

答案 2 :(得分:9)

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

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

值得注意的是split(_:)将返回SubSequence个字符(基本上是Slice),因此需要转换回字符串数组,这通常更有用。我在下面使用flatMap(String.init)完成了此操作 - UTF16View上的String初始化工具可以使用,因此使用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标准Character的集合。

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

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

答案 3 :(得分:6)

let test1 = "Line1\n\rLine2\nLine3\rLine4"
let t1 = test1.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet())
let t2 = t1.filter{ $0 != "" }
let t3 = t1.filter{ !$0.isEmpty }

答案 4 :(得分: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的答案。阅读这些答案以获取更多细节。

答案 5 :(得分:3)

为了记录,Swift的基金会CharacterSet可以在分割中使用:

替代方案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)
    }
}

答案 6 :(得分:1)

  

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

你几乎就在那里 - 这只是尾随的闭包,这里不同:

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:在尾随结束时删除了不必要的额外括号

答案 7 :(得分:0)

Swift 4:

我建议您先将CSV保存到字符串中,如果您还没有完成它,那么&#34;清理&#34;删除不必要的回车的字符串

        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个项目的数组:

<强> [&#34;线路1&#34;&#34;线2&#34;&#34;行3&#34;]

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

[&#34;线路1,线路2,行3&#34;&#34; LINEA,LineB,LineC&#34;]

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

答案 8 :(得分:0)

     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