读写文本文件中的字符串

371

我需要读写文本文件中的数据,但一直没有搞清楚如何操作。

我在 Swift 的 iBook 中找到了这个代码示例,但仍然不知道如何读写数据。

import Cocoa

class DataImporter {
    /*
    DataImporter is a class to import data from an external file.
    The class is assumed to take a non-trivial amount of time to initialize.
    */
    var fileName = "data.txt"
    // the DataImporter class would provide data importing functionality here
}

class DataManager {
    @lazy var importer = DataImporter()
    var data = String[]()
    // the DataManager class would provide data management functionality here
}

let manager = DataManager()
manager.data += "Some data"
manager.data += "Some more data"
// the DataImporter instance for the importer property has not yet been created”

println(manager.importer.fileName)
// the DataImporter instance for the importer property has now been created
// prints "data.txt”    

var str = "Hello World in Swift Language."
21个回答

635

读写操作应使用可写入的位置,例如文档目录。下面是一个演示如何读写简单字符串的代码示例,你可以在Playground中进行测试。

Swift 3.x - 5.x

let file = "file.txt" //this is the file. we will write to and read from it

let text = "some text" //just a text

if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {

    let fileURL = dir.appendingPathComponent(file)

    //writing
    do {
        try text.write(to: fileURL, atomically: false, encoding: .utf8)
    }
    catch {/* error handling here */}

    //reading
    do {
        let text2 = try String(contentsOf: fileURL, encoding: .utf8)
    }
    catch {/* error handling here */}
}

Swift 2.2

let file = "file.txt" //this is the file. we will write to and read from it

let text = "some text" //just a text

if let dir = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true).first {
    let path = NSURL(fileURLWithPath: dir).URLByAppendingPathComponent(file)

    //writing
    do {
        try text.writeToURL(path, atomically: false, encoding: NSUTF8StringEncoding)
    }
    catch {/* error handling here */}

    //reading
    do {
        let text2 = try NSString(contentsOfURL: path, encoding: NSUTF8StringEncoding)
    }
    catch {/* error handling here */}
}

Swift 1.x

let file = "file.txt"

if let dirs : [String] = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true) as? [String] {
    let dir = dirs[0] //documents directory
    let path = dir.stringByAppendingPathComponent(file);
    let text = "some text"

    //writing
    text.writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding, error: nil);

    //reading
    let text2 = String(contentsOfFile: path, encoding: NSUTF8StringEncoding, error: nil)
}

3
let text2 = String(contentsOfFile: path) // XCode 6.0 - Matt Frear
你为什么在writeToFile中将atomically设置为false - Crashalot
在Swift 2.0中,不需要进行as? [String]的强制转换。 - fnc12
9
应该将此部分移除,因为代码在新版本的 Swift 中无法运行。 - user3325915
1
@billy_b29 这行代码之后的代码://reading 正是这样做的。 - Adam
显示剩余15条评论

112
假设您已将文本文件data.txt移动到Xcode项目中(使用拖放并检查“必要时复制文件”),您可以像在Objective-C中那样执行以下操作:
let bundle = NSBundle.mainBundle()
let path = bundle.pathForResource("data", ofType: "txt")        
let content = NSString.stringWithContentsOfFile(path) as String

println(content) // prints the content of data.txt

更新:
要从Bundle(iOS)中读取文件,您可以使用以下方法:

let path = NSBundle.mainBundle().pathForResource("FileName", ofType: "txt")
var text = String(contentsOfFile: path!, encoding: NSUTF8StringEncoding, error: nil)!
println(text)

Swift 3更新:

let path = Bundle.main.path(forResource: "data", ofType: "txt") // file path for file "data.txt"
var text = String(contentsOfFile: path!, encoding: NSUTF8StringEncoding, error: nil)!

针对 Swift 5

let path = Bundle.main.path(forResource: "ListAlertJson", ofType: "txt") // file path for file "data.txt"
let string = try String(contentsOfFile: path!, encoding: String.Encoding.utf8)

3
针对iOS项目,"stringWithContentsOfFile"方法不可用(自iOS 7起被弃用)。 - alttag
1
与iOS项目无关,它已被废弃,无法在Xcode 6.1(包括Mac OS X)中使用。 - Leo Dabus
2
你可以使用String(contentsOfFile: ...)。 - shim
1
类似的解决方案在iOS 10 Swift 3中使用bundle 这里 - Timeless

76

新的更简单和推荐使用的方法: Apple建议使用URL来处理文件,这里的其他解决方案似乎已经过时了(请参见下面的评论)。 以下是使用URL进行读写的新简单方法:

适用于Swift 5+、4和3.1

import Foundation  // Needed for those pasting into Playground

let fileName = "Test"
let dir = try? FileManager.default.url(for: .documentDirectory, 
      in: .userDomainMask, appropriateFor: nil, create: true)

guard let fileURL = dir?.appendingPathComponent(fileName).appendingPathExtension("txt") else {
    fatalError("Not able to create URL")
}
    
// Writing to the file named Test
let outString = "Write this text to the file"
do {
    try outString.write(to: fileURL, atomically: true, encoding: .utf8)
} catch {
    assertionFailure("Failed writing to URL: \(fileURL), Error: " + error.localizedDescription)
}
    
// Reading it back from the file
var inString = ""
do {
    inString = try String(contentsOf: fileURL)
} catch {
    assertionFailure("Failed reading from URL: \(fileURL), Error: " + error.localizedDescription)
}
print("Read from the file: \(inString)")

1
你能提供一个苹果推荐这种方式的参考吗?或者你能详细解释一下为什么这是推荐的方式吗? - Andrej
8
"URL对象是指向本地文件的首选方式。大多数读取或写入文件数据的对象都有接受NSURL对象而非文件路径名作为文件引用的方法。" https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSURL_Class/ - Sverrisson
1
你不必将错误强制转换为NSError,甚至不必使用“catch let error。”你只需使用catch,就可以免费获得错误变量。 - cuomo456
@user1593861 如果你已经有一个字符串并且它不是非常大,那么这是最简单的方法。写入是异步完成的。如果你有一个非常大的字符串,那么你可以使用流将其写入文件,但这超出了本答案的范围,请参见 https://stackoverflow.com/questions/36120854/swift-writing-a-byte-stream-to-file 。感谢您的好评 :) - Sverrisson
1
@Alshcompiler create: true 告诉 FileManager 如果目录不存在就创建它,而不是失败。 - Sverrisson
显示剩余8条评论

71

Xcode 8.x • Swift 3.x或更高版本

do {
    // get the documents folder url
    if let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
        // create the destination url for the text file to be saved
        let fileURL = documentDirectory.appendingPathComponent("file.txt")
        // define the string/text to be saved
        let text = "Hello World !!!"
        // writing to disk 
        // Note: if you set atomically to true it will overwrite the file if it exists without a warning
        try text.write(to: fileURL, atomically: false, encoding: .utf8)
        print("saving was successful")
        // any posterior code goes here
        // reading from disk
        let savedText = try String(contentsOf: fileURL)
        print("savedText:", savedText)   // "Hello World !!!\n"
    }
} catch {
    print("error:", error)
}

“没有这个文件”最常见的错误是什么?因为我将我的.txt文件添加到项目导航器中,然后尝试打开它们时会出现此消息。(在桌面上创建它们并将它们拖到项目导航器中) - Darvas

31

Xcode 8,Swift 3读取应用程序捆绑包中文件的方法:

if let path = Bundle.main.path(forResource: filename, ofType: nil) {
    do {
        let text = try String(contentsOfFile: path, encoding: String.Encoding.utf8)
        print(text)
    } catch {
        printError("Failed to read text from \(filename)")
    }
} else {
    printError("Failed to load file from app bundle \(filename)")
} 

这是一个方便的复制粘贴扩展程序。
public extension String {
    func contentsOrBlank()->String {
        if let path = Bundle.main.path(forResource:self , ofType: nil) {
            do {
                let text = try String(contentsOfFile:path, encoding: String.Encoding.utf8)
                return text
                } catch { print("Failed to read text from bundle file \(self)") }
        } else { print("Failed to load file from bundle \(self)") }
        return ""
    }
    }

举个例子

let t = "yourFile.txt".contentsOrBlank()

你几乎总是需要一个行数组:

let r:[String] = "yourFile.txt"
     .contentsOrBlank()
     .characters
     .split(separator: "\n", omittingEmptySubsequences:ignore)
     .map(String.init)

2
我粘贴了一个方便的扩展名 @crashalot - 随意删除,谢谢。 - Fattie
2
@Alshcompiler 不行!你不能将文件写入捆绑包中。 - Sverrisson
我在谈论从文件中读取数据,如果文件在项目文件中,这是唯一有效的解决方案。 - Mostafa Sultan

11

我希望展示给您的是第一部分,即读取。以下是如何简单地读取:

Swift 3:

let s = try String(contentsOfFile: Bundle.main.path(forResource: "myFile", ofType: "txt")!)

Swift 2:

let s = try! String(contentsOfFile: NSBundle.mainBundle().pathForResource("myFile", ofType: "txt")!)

5

在Swift > 4.0中读取文件的最简单方法

 let path = Bundle.main.path(forResource: "data", ofType: "txt") // file path for file "data.txt"
        do {
            var text = try String(contentsOfFile: path!)
        }
        catch(_){print("error")}
    }

4

您可能会发现这个工具不仅可以在Swift中从文件中读取,还可以解析输入内容:https://github.com/shoumikhin/StreamScanner

只需指定文件路径和数据分隔符,就像这样:

import StreamScanner

if let input = NSFileHandle(forReadingAtPath: "/file/path")
{
    let scanner = StreamScanner(source: input, delimiters: NSCharacterSet(charactersInString: ":\n"))  //separate data by colons and newlines

    while let field: String = scanner.read()
    {
        //use field
    }
}

希望这能帮到你。

4
这在Linux上适用于Swift 3.1.1:
import Foundation

let s = try! String(contentsOfFile: "yo", encoding: .utf8)

3
为了避免混淆并增加便利性,我创建了两个函数来读取和写入文档目录中的字符串。以下是这两个函数:
func writeToDocumentsFile(fileName:String,value:String) {
    let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as! NSString
    let path = documentsPath.stringByAppendingPathComponent(fileName)
    var error:NSError?
    value.writeToFile(path, atomically: true, encoding: NSUTF8StringEncoding, error: &error)
}

func readFromDocumentsFile(fileName:String) -> String {
    let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as! NSString
    let path = documentsPath.stringByAppendingPathComponent(fileName)
    var checkValidation = NSFileManager.defaultManager()
    var error:NSError?
    var file:String

    if checkValidation.fileExistsAtPath(path) {
        file = NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding, error: nil) as! String
    } else {
        file = "*ERROR* \(fileName) does not exist."
    }

    return file
}

这里是它们使用的一个例子:
writeToDocumentsFile("MyText.txt","Hello world!")

let value = readFromDocumentsFile("MyText.txt")
println(value)  //Would output 'Hello world!'

let otherValue = readFromDocumentsFile("SomeText.txt")
println(otherValue)  //Would output '*ERROR* SomeText.txt does not exist.'

希望这能帮到你! Xcode 版本:6.3.2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接