在Swift中读取文件,iOS Playground

14

在搜索了许多(许多!)Swift Playgrounds问题以制作此代码后,我仍然遇到了困难。

我已经将一个文本文件放置在包内容的Resources文件夹中,并且它出现为别名(链接)在由Playground生成的运行临时文件中(/var/folders/ ...)。

import UIKit

let bundle = NSBundle.mainBundle()

let myFilePath = bundle.pathForResource("dict1", ofType: "txt")

println(myFilePath)    // <-- this is correct, there is a shortcut to the Resource file at this location

var error:NSError?

var content = String(contentsOfFile:myFilePath!, encoding:NSUTF8StringEncoding, error: &error)

println(content!)  // <-- this is *NOT* the file contents [EDIT: see later note]

// Demonstrate there's no error
if let theError = error {
    print("\(theError.localizedDescription)")
} else {
    print("No error")
}
问题在于,content 在游乐场输出中显示为 Some "apple\ngame\nhow\nswift\ntoken",而不是预期的文件内容。
它找到了文件,因为如果我更改文件名,它就会出错。有什么建议可以获取文件内容吗?
Xcode 6.1
编辑:实际问题是我没有预料到游乐场输出(包括 println)会被转义。由于疲劳和其他愚蠢的行为,我误认为存在问题,但实际上并没有。
有趣的是,游乐场中似乎不是所有内容都被转义了:
println("foo\nbar")    // Outputs "foo\nbar", escaped
println("\\n")         // Outputs "\n", unescaped

1
如果我把“dict1.txt”文件放到适当的位置,那段代码对我来说可以正常工作。你的“dict1.txt”文件中有什么内容?它与你在“content”变量中看到的有什么不同? - Mike S
1
@MikeS,你的简单问题让我惊奇地找到了(显而易见的)答案。当然,'content'匹配文件是正确的。我不应该在疲惫时编码。让我困惑的是转义的 '\n' ,使内容看起来像一个路径。 - alttag
在我的学习过程中,println 的游乐场输出与控制台输出不相等。有关控制台的更多信息,请参见此问题 - alttag
2个回答

12

你可以尝试创建一个用于打开和保存文件的类:

编辑/更新:Swift 5或更高版本

class File {
    class func open(
        _ path: String,
        encoding: String.Encoding = .utf8
    ) throws -> String {
        guard FileManager.default.fileExists(atPath: path) else {
            throw NSError(
                domain: "NSCocoaErrorDomain",
                code: 260,
                userInfo: [
                    "NSUnderlyingError": #"Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory""#,
                    "NSFilePath": path
                ]
            ) as Error
        }
        return try String(
            contentsOfFile: path,
            encoding: encoding
        )
    }

    class func save(
        _ path: String,
        _ content: String,
        encoding: String.Encoding = .utf8
    ) throws {
        try content.write(
            toFile: path,
            atomically: true,
            encoding: encoding
        )
    }
}

用法:File.save

let stringToSave: String = "Your text"

if let fileURL = FileManager.default.urls(
    for: .desktopDirectory,
    in: .userDomainMask
).first?.appendingPathComponent("file.txt") {
    do {
        try File.save(fileURL.path, stringToSave)
        print("file saved")
    } catch {
        print("Error saving file:", error)
    }
}

用法:File.open

if let fileURL = FileManager.default.urls(
    for: .desktopDirectory,
    in: .userDomainMask
).first?.appendingPathComponent("file.txt") {
    do {
        let loadedText = try File.open(fileURL.path)
        print("Loaded text:", loadedText)
    } catch {
        print("Error reading file:", error)
    }
} 

或者,如果您更喜欢扩展StringProtocol


extension StringProtocol {
    func open(from directory: FileManager.SearchPathDirectory = .documentDirectory,
              in domain: FileManager.SearchPathDomainMask = .userDomainMask,
              encoding: String.Encoding = .utf8) throws -> String {
        let directory = try FileManager.default.url(
            for: directory,
               in: domain,
               appropriateFor: nil,
               create: true
        )
        return try String(
            contentsOf: directory.appendingPathComponent(.init(self)),
            encoding: encoding
        )
    }
    func save(as fileName: String,
              to directory: FileManager.SearchPathDirectory = .documentDirectory,
              in domain: FileManager.SearchPathDomainMask = .userDomainMask,
              encoding: String.Encoding = .utf8) throws {
        let directory = try FileManager.default.url(
            for: directory,
               in: domain,
               appropriateFor: nil,
               create: true
        )
        try write(to: directory.appendingPathComponent(fileName),
                  atomically: true,
                  encoding: encoding)
    }
}

iOS使用方法(保存/加载到文档目录):

let stringToSave: String = "Your text"
let fileName = "file.txt"
do {
    try stringToSave.save(as: fileName)
    print("Text saved!!!")
    let loadedText = try fileName.open()
    print("Text loaded:", loadedText)
} catch {
    print("Error:", error)
}

使用 macOS(保存/加载桌面目录):

let string = "Your text"
let fileName = "file.txt"
do {
    try string.save(as: fileName, to: .desktopDirectory)
    print("Text saved!!!")
    let loadedText = try fileName.open(from: .desktopDirectory)
    print("Text loaded:", loadedText)
} catch {
    print("Error:", error)
}

6
我曾经看到过用TextEdit从.rtf文件创建的.txt文件出现了这个问题。
我使用类似于你的代码将一个text.txt文件加载到playground的资源文件夹中。文件内容为“hello there”,是通过更改扩展名将.rtf文件转换为.txt文件得到的。请注意,这种情况并不罕见。
let path = NSBundle.mainBundle().pathForResource("text", ofType: "txt")//or rtf for an rtf file
var text = String(contentsOfFile: path!, encoding: NSUTF8StringEncoding, error: nil)!
println(text)

输出结果为:

{\rtf1\ansi\ansicpg1252\cocoartf1343\cocoasubrtf140 {\fonttbl\f0\fswiss\fcharset0 Helvetica;} {\colortbl;\red255\green255\blue255;} \margl1440\margr1440\vieww10800\viewh8400\viewkind0 \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural

\f0\fs24 \cf0 hello there}

因此,“hello there”被嵌入其中。这是.rtf文件中所有布局信息的问题。

我回到TextEdit并创建了一个真正的.txt文件。打开文件-格式|制作纯文本

现在,同样的代码给出了控制台输出“hello there”。


有用的信息,但显然这个问题并没有受到RTF问题的影响。尽管我对自己犯的错误感到很愚蠢,但我肯定不会犯这种错误! - alttag

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