我有一个从文本文件中得到的字符串。
文本文件:
Line 1
Line 2
Line 3
...
我想将其转换为数组,每行一个数组元素。
[ "Line 1", "Line 2", "Line 3", ... ]
根据文件的保存方式,字符串可以采用以下形式之一:
string = "Line 1nLine 2nLine 3n..."
其中n
换行符string = "Line 1rnLine 2rnLine 3rn..."
其中r
是回车符。
据我了解,n
在今天的Apple/Linux中常用,而rn
则在Windows中使用。
如何在任何换行符处拆分字符串以获得没有任何空元素的字符串数组?
更新
下面有几种解决方案。在这一点上,我没有任何令人信服的理由选择一个比其他的更正确。可能影响选择的一些因素可能是(1(它的"Swift"程度和(2(对于非常长的字符串有多快。您可以通过对其中一个或多个投赞成票和/或发表评论来提供反馈。
在这里查看我的总结答案
Swift 5.2 或更高版本
您可以使用新的Character
属性拆分String
isNewline
:
let sentence = "Line 1nLine 2nLine 3n"
let lines = sentence.split(whereSeparator: .isNewline)
print(lines) // "["Line 1", "Line 2", "Line 3"]n"
<小时 />您还可以扩展 StringProtocol 并创建行实例属性,以将字符串行分解为子序列:
extension StringProtocol {
var lines: [SubSequence] { split(whereSeparator: .isNewline) }
}
<小时 /> let sentence = "Line 1nLine 2rnLine 3n"
for line in sentence.lines {
print(line)
}
let lines = sentence.lines // ["Line 1", "Line 2", "Line 3"]
<小时 /><小时 />原始答案
可以使用字符串方法枚举行:
枚举字符串中的所有行。
Swift 3 或更高版本
let sentence = "Line 1nLine 2nLine 3n"
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 4nLine 5nLine 6n"
let sentence2Lines = sentence2.lines
print(sentence2Lines) // "["Line 4", "Line 5", "Line 6"]n"
let sentence3 = "Line 7rnLine 8rnLine 9rn"
let sentence3Lines = sentence3.lines
print(sentence3Lines) // "["Line 7", "Line 8", "Line 9"]n"
在 Xcode 8.2, Swift 3.0.1 中:
使用 NSString 方法组件(分隔 dBy:(
let text = "line1nline2"
let array = text.components(separatedBy: CharacterSet.newlines)
或者使用字符串方法枚举行,如Leo Dabus
的答案
在 Swift 2 中,顶级 split
函数现在是 CollectionType
上的一个方法(String
的每个"字符视图"都符合(。该方法有两个版本,您希望将闭包作为谓词的版本来指示是否应将给定元素视为分隔符。
您可以使用 string.utf16
从字符串中获取字符集合作为 UTF16 字符的集合,使它们与NSCharacterSet
API 兼容。这样,我们可以很容易地在闭包内部检查字符串中的给定字符是否是换行符集的成员。
值得注意的是,split(_:)
将返回SubSequence
字符(基本上是一个Slice
(,因此它需要转换回通常更有用的字符串数组。我在下面使用 flatMap(String.init)
完成了此操作 - String
上的 UTF16View
初始化器是失败的,因此使用 flatMap
将忽略可能返回的任何nil
值,确保您返回非可选字符串数组。
因此,对于一个不错的类似 Swift 的方式来做到这一点:
let str = "Line 1nLine 2rnLine 3n"
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
s。
let lines = str.characters.split { $0 == "n" || $0 == "rn" }.map(String.init)
Swift 能够将"rn"
视为单个扩展的字形簇,将其用作用于比较的单个Character
,而不是创建String
。另请注意,用于从Character
创建字符串的初始化器是不可失败的,因此我们可以只使用 map
.
这个答案是对已经给出的其他解决方案的总结。它来自我更完整的答案,但在此处提供实际方法选择会很有用。
新行通常使用n
字符进行,但也可以使用rn
(从保存在Windows中的文件(进行。
解决 方案
1. componentsSeparatedByCharactersInSet
let multiLineString = "Line 1nLine 2rnLine 3n"
let newlineChars = NSCharacterSet.newlineCharacterSet()
let lineArray = multiLineString.componentsSeparatedByCharactersInSet(newlineChars).filter{!$0.isEmpty}
// "[Line 1, Line 2, Line 3]"
如果不使用filter
,则rn
将生成一个空数组元素,因为它被计为两个字符,因此在同一位置将字符串分隔两次。
2. split
let multiLineString = "Line 1nLine 2rnLine 3n"
let newlineChars = NSCharacterSet.newlineCharacterSet()
let lineArray = multiLineString.utf16.split { newlineChars.characterIsMember($0) }.flatMap(String.init)
// "[Line 1, Line 2, Line 3]"
或
let multiLineString = "Line 1nLine 2rnLine 3n"
let lineArray = multiLineString.characters.split { $0 == "n" || $0 == "rn" }.map(String.init)
// "[Line 1, Line 2, Line 3]"
在这里rn
被计为单个 Swift 字符(扩展的字形簇(
3. enumerateLines
let multiLineString = "Line 1nLine 2rnLine 3n"
var lineArray = [String]()
multiLineString.enumerateLines { (line, stop) -> () in
lineArray.append(line)
}
// "[Line 1, Line 2, Line 3]"
有关enumerateLine
语法的更多信息,另请参阅此答案。
笔记:
- 多行字符串通常不会同时混合
rn
和n
但我在这里这样做是为了表明这些方法可以处理这两种格式。 -
NSCharacterSet.newlineCharacterSet()
是定义为 (U+000A–U+000D、U+0085( 的换行符,包括r
和n
。 - 这个答案是我上一个问题的答案的总结。阅读这些答案以获取更多详细信息。
let test1 = "Line1nrLine2nLine3rLine4"
let t1 = test1.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet())
let t2 = t1.filter{ $0 != "" }
let t3 = t1.filter{ !$0.isEmpty }
作为记录,Swift 的基础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)
}
}
如何在任何换行符处拆分字符串以获得没有任何空元素的字符串数组?
你几乎在那里 - 这只是这里的尾随闭包不同:
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:删除了尾随闭包处不必要的额外括号
Swift 4:
如果您还没有这样做,我建议您先将 CSV 保存到字符串中,然后通过删除不必要的回车符来"清理"字符串
let dataString = String(data: yourData!, encoding: .utf8)!
var cleanFile = dataString.replacingOccurrences(of: "r", with: "n")
cleanFile = cleanFile.replacingOccurrences(of: "nn", with: "n")
上面将为您提供一个格式最理想的字符串,然后您可以使用作为分隔符来分隔字符串:
let csvStrings = cleanFile.components(separatedBy: ["n"])
现在您有一个包含 3 个项目的数组,例如:
["第 1 行"、"第 2 行"、"第 3 行"]
我正在使用CSV文件,完成此操作后,我将项目拆分为组件,因此,如果您的项目是这样的:
["1号线,2号线,3号线","A线,B线,C线"]
let component0 = csvStrings[0].components(separatedBy: [","]) // ["Line1","Line2","Line3"]
let component1 = csvStrings[1].components(separatedBy: [","]) // ["LineA","LineB","LineC"]
选项 1:
let getName = "Davender+Verma"
let cleanFile = getName.replacingOccurrences(of: "+", with: "+n")
self.upcomingViewPetName.text = cleanFile
// Output
Davender+
verma
选项 2:
let getName = "Davender+Verma"
let cleanFile = getName.replacingOccurrences(of: "+", with: "n")
self.upcomingViewPetName.text = cleanFile
//Output:
Davender
verma