2017-10-28 85 views
0

Playground中的測試我讀取一個字符串數組中的整個文件,每行一個字符串。 但我需要的只是一個特定的行:如何在swift4中讀取特定文件的行?

let dir = try? FileManager.default.url(for: .documentDirectory, 
            in: .userDomainMask, appropriateFor: nil, create: true) 
let fileURL = dir!.appendingPathComponent("test").appendingPathExtension("txt") 
let text: [String] = try String(contentsOf: fileURL).components(separatedBy: NSCharacterSet.newlines) 
let i = 2 // computed before, here to simplify 
print(text[i]) 

有一種方法,以避免讀取完整的大文件?

+0

https://stackoverflow.com/questions/24581517/read-a-file-url-line-by-line-in-swift#24648951 –

+0

在發佈新問題之前,您應該嘗試搜索您的答案。 –

+0

我不確定你的意思是「避免閱讀完整的大文件」。你有一個'String'對象的數組。這是問題嗎?你不想將整個文件讀入數組中?否則,如果你知道具體的行號,你的'text [i]'選項就可以工作。或者,你是否問通過循環訪問數組來找到某行?而且,如果是這樣的話,請添加你如何知道你達到了你想要的範圍。 – leanne

回答

0

我猜你的意思是你想檢索索引而不用手動搜索數組,例如for-in循環。

在Swift 4中,您可以使用Array.index(where:)結合StringProtocol的通用contains(_:)函數來查找您要查找的內容。

讓我們假設您正在尋找包含text: [String]數組中文本「重要內容」的第一行。

你可以使用:

text.index(where: { $0.contains("important stuff") }) 

在幕後,斯威夫特循環查找的文字,但內置的增強,這應該不是通過text陣列手動循環有更好的表現。


注意,該搜索的結果可能是nil如果沒有匹配的線都存在。因此,你需要確保它不是nil使用結果前:

力解開的結果(冒着可怕的fatal error: unexpectedly found nil while unwrapping an Optional value):

print(text[lineIndex!) 

或者,使用if let聲明:

if let lineIndex = stringArray.index(where: { $0.contains("important stuff") }) { 
    print(text[lineIndex]) 
} 
else { 
    print("Sorry; didn't find any 'important stuff' in the array.") 
} 

或使用guard聲明:

guard let lineIndex = text.index(where: {$0.contains("important stuff")}) else { 
    print("Sorry; didn't find any 'important stuff' in the array.") 
    return 
} 
print(text[lineIndex]) 
0

要查找特定的行而不讀取整個文件,可以使用此StreamReader答案。它包含了在Swift 3中工作的代碼。我在Swift 4中對它進行了測試:請參閱我的GitHub回購,TEST-StreamReader,瞭解我的測試代碼。

您仍然需要循環才能到達正確的位置,但是一旦檢索到該行,就會返回break循環。

下面是從StreamReader類SO回答:

class StreamReader { 

    let encoding : String.Encoding 
    let chunkSize : Int 
    var fileHandle : FileHandle! 
    let delimData : Data 
    var buffer : Data 
    var atEof : Bool 

    init?(path: String, delimiter: String = "\n", encoding: String.Encoding = .utf8, 
      chunkSize: Int = 4096) { 

     guard let fileHandle = FileHandle(forReadingAtPath: path), 
      let delimData = delimiter.data(using: encoding) else { 
       return nil 
     } 
     self.encoding = encoding 
     self.chunkSize = chunkSize 
     self.fileHandle = fileHandle 
     self.delimData = delimData 
     self.buffer = Data(capacity: chunkSize) 
     self.atEof = false 
    } 

    deinit { 
     self.close() 
    } 

    /// Return next line, or nil on EOF. 
    func nextLine() -> String? { 
     precondition(fileHandle != nil, "Attempt to read from closed file") 

     // Read data chunks from file until a line delimiter is found: 
     while !atEof { 
      if let range = buffer.range(of: delimData) { 
       // Convert complete line (excluding the delimiter) to a string: 
       let line = String(data: buffer.subdata(in: 0..<range.lowerBound), encoding: encoding) 
       // Remove line (and the delimiter) from the buffer: 
       buffer.removeSubrange(0..<range.upperBound) 
       return line 
      } 
      let tmpData = fileHandle.readData(ofLength: chunkSize) 
      if tmpData.count > 0 { 
       buffer.append(tmpData) 
      } else { 
       // EOF or read error. 
       atEof = true 
       if buffer.count > 0 { 
        // Buffer contains last line in file (not terminated by delimiter). 
        let line = String(data: buffer as Data, encoding: encoding) 
        buffer.count = 0 
        return line 
       } 
      } 
     } 
     return nil 
    } 

    /// Start reading from the beginning of file. 
    func rewind() -> Void { 
     fileHandle.seek(toFileOffset: 0) 
     buffer.count = 0 
     atEof = false 
    } 

    /// Close the underlying file. No reading must be done after calling this method. 
    func close() -> Void { 
     fileHandle?.closeFile() 
     fileHandle = nil 
    } 
} 

extension StreamReader : Sequence { 
    func makeIterator() -> AnyIterator<String> { 
     return AnyIterator { 
      return self.nextLine() 
     } 
    } 
}