如何在Swift Playground中读取文件

49

我正在使用Swift playground尝试读取一个文本文件,代码如下:

let dirs : String[]? =    NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true) as? String[]

if (dirs != nil) {
    let directories:String[] = dirs!;
    let dir = directories[0]; //documents directory
    let path = dir.stringByAppendingPathComponent(file);

    //read
    let content = String.stringWithContentsOfFile(path, encoding: NSUTF8StringEncoding, error: nil)
}

但这样做不会报错。似乎第一行代码阻止了 Playground 输出它以下的任何内容。


3
stringWithContentsOfFile 有一个错误参数,请检查它包含什么。 - Alexander
你应该使用条件解包 - if let directories = dirs {。这样可以节省一步。 - Kevin
11个回答

81

您还可以将文件放入您的 playground 资源中。 操作步骤如下:使用 CMD + 1 显示项目导航器。 将文件拖放到资源文件夹中。 然后读取文件:

在 Xcode 6.4 和 Swift 1.2 上:

var error: NSError?
let fileURL = NSBundle.mainBundle().URLForResource("Input", withExtension: "txt")
let content = String(contentsOfURL: fileURL!, encoding: NSUTF8StringEncoding, error: &error)

在 Xcode 7 和 Swift 2 上:

let fileURL = NSBundle.mainBundle().URLForResource("Input", withExtension: "txt")
let content = try String(contentsOfURL: fileURL!, encoding: NSUTF8StringEncoding)

在Xcode 8和Swift 3上:

let fileURL = Bundle.main.url(forResource: "Input", withExtension: "txt")
let content = try String(contentsOf: fileURL!, encoding: String.Encoding.utf8)

如果文件包含二进制数据,则可使用 NSData(contentsOfURL: fileURL!)Data(contentsOf: fileURL!)(适用于 Swift 3)。


39

虽然已经提供了一个快速解决方案,但还有更好的解决办法。

每次打开 playground 时,都会分配一个新的容器。这意味着使用普通的目录结构,您每次都需要将所需文件复制到新容器中。

相反,在容器内部有一个指向 Shared Playground Data 目录的符号链接(/Users/用户名/Documents/Shared Playground Data)。当重新打开 playground 时,此链接仍然存在,并且可以从多个 playground 中访问。

您可以使用 XCPlayground 来访问此共享文件夹。

import XCPlayground

let path = XCPlaygroundSharedDataDirectoryURL.appendingPathComponent("foo.txt")

官方文档可以在这里找到:XCPlayground模块参考 关于如何按照每个playground来组织此目录的酷帖子:Swift,Playgrounds和XCPlayground

更新:对于Swift 4.2,请使用playgroundSharedDataDirectory。不需要导入任何内容。 看起来像这样:

let path = playgroundSharedDataDirectory.appendingPathComponent("file")

Swift 5 更新:你需要 import PlaygroundSupport 来访问 playgroundSharedDataDirectory 全局变量。


6
很棒的答案,绝妙的解决方案! 我不得不创建~/Documents/Shared Plaground Data,但之后所有的都按预期工作了。 谢谢! - 0ff
1
以上评论中有错别字,但它能够正常运行。在文档中创建该文件夹,将任何内容放入其中,即可在XCPSharedDataDirectoryPath中使用。 - Max MacLeod
3
XCPSharedDataDirectoryPath已被废弃,建议使用XCPlaygroundSharedDataDirectoryURL - Mr Rogers
1
XCode 7.3。创建了“〜/文档/共享游乐场数据”目录,将我的文件复制到那里,然后在Playground中使用以下代码:在顶部导入“import XCPlayground”,并在需要打开文件的位置使用“let path = XCPlaygroundSharedDataDirectoryURL.URLByAppendingPathComponent(“myfile.txt”)”。请注意,我尝试使用“NSData(contentsOfFile :)”,但是使用上述方法,我只需要切换到“NSData(contentsOfURL :)”。像魅力一样工作! - LNI
3
对于Swift 4.2,请使用playgroundSharedDataDirectory。不需要导入任何内容--但是您需要导入PlaygroundSupport(Swift 5 / Xcode 10.2)。 - Jere Käpyaho
显示剩余3条评论

32
1. 访问位于您的 Playground 的 Resources 文件夹中的文件

在 Swift 3 中,Bundle 有一个名为 url(forResource:withExtension:) 的方法。 url(forResource:withExtension:) 的声明如下:

func url(forResource name: String?, withExtension ext: String?) -> URL?

返回由指定名称和文件扩展名标识的资源的文件URL。
您可以使用`url(forResource:withExtension:)`来读取位于iOS或Mac Playground的`Resources`文件夹中的json文件的内容。
import Foundation

do {
    guard let fileUrl = Bundle.main.url(forResource: "Data", withExtension: "json") else { fatalError() }
    let data = try Data(contentsOf: fileUrl)
    let json = try JSONSerialization.jsonObject(with: data, options: [])
    print(json)
} catch {
    print(error)
}    

2. 访问位于计算机的`~/Documents/Shared Playground Data`文件夹中的文件
使用Swift 3,`PlaygroundSupport`模块提供了一个名为`playgroundSharedDataDirectory`的全局常量。`playgroundSharedDataDirectory`的声明如下:
let playgroundSharedDataDirectory: URL

包含所有游乐场共享数据的目录路径。
您可以使用`playgroundSharedDataDirectory`来读取位于您计算机的macOS游乐场的`~/Documents/Shared Playground Data`文件夹中的json文件的内容。
import Foundation
import PlaygroundSupport

do {
    let fileUrl = PlaygroundSupport.playgroundSharedDataDirectory.appendingPathComponent("Data.json")        
    let data = try Data(contentsOf: fileUrl)
    let json = try JSONSerialization.jsonObject(with: data, options: [])
    print(json)
} catch {
    print(error)
}

13

Swift 3(Xcode 8)

下面的代码适用于iOS和macOS playground。文本文件(例如"MyText.txt")必须在playground的Resources目录中。 (注意:您可能需要打开导航窗口才能查看playground的目录结构。)

import Foundation

if let fileURL = Bundle.main.url(forResource:"MyText", withExtension: "txt")
{
    do {
        let contents = try String(contentsOf: fileURL, encoding: String.Encoding.utf8)
        print(contents)
    } catch {
        print("Error: \(error.localizedDescription)")
    }
} else {
    print("No such file URL.")
}

8

对我来说这是有效的。我改变的唯一一件事是明确文件名(在您的示例中暗示了) - 也许您在“file”变量的屏幕外定义中有一个拼写错误?

let dirs = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true) as? [String]

let file = "trial.txt" // My change to your code - yours is presumably set off-screen
if let directories = dirs {
  let dir = directories[0]; //documents directory
  let path = dir.stringByAppendingPathComponent(file);

  //read
  let content = NSString(contentsOfFile: path, usedEncoding: nil, error: nil)
  // works...
}

更新Swift 4.2版本

@raistlin提出,现在应该使用以下代码:

let dirs = NSSearchPathForDirectoriesInDomains(
              FileManager.SearchPathDirectory.documentDirectory,
              FileManager.SearchPathDomainMask.userDomainMask,
              true)

或者更简洁地说:
let dirs = NSSearchPathForDirectoriesInDomains(.documentDirectory,
                                .userDomainMask, true)

谢谢。我刚刚在同事的电脑上尝试了一下,它可以工作。我正在使用Yosemite beta,所以我猜这就是我的问题。 - Greg
1
问题出在我创建了一个iOS playground。 - Greg
1
Swift 4.2 版本为:NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true) - raistlin

3
  1. 选择.playground文件。

  2. 打开实用程序检查器,在playground中按opt-cmd-1打开文件检查器。您应该在右侧看到playground。如果没有选中它,请按cmd-1打开项目导航器,然后单击playground文件。

  3. 在Playground设置中的'Resource Path'下选择'Relative To Playground'和平台为OSX。


尝试将PlayGround设置中的平台更改为OSX,它仅适用于OSX。 - Yatheesha
谢谢,看起来平台出了问题,但只有重新从头开始创建新的游乐场才能正常工作。 - Greg

1
在Mavericks操作系统上使用Xcode 6.0.1,您也可以使用iOS平台进行阅读。
import UIKit
let dirs : [String]? =    NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true) as? [String]
let myDir = "/Shared Playground Data"

let file = "README.md" // My change to your code - yours is presumably set off-screen
if (dirs != nil) {
  let directories:[String] = dirs!;
  let dir = directories[0] + myDir; // iOS playground documents directory
  let path = dir.stringByAppendingPathComponent(file);

  //read
  let content = String.stringWithContentsOfFile(path, encoding: NSUTF8StringEncoding, error: nil)
  // works...
  println(content!)
}

请记住,在您的文档目录下需要创建一个名为"Shared Playground Data"的目录。在我的情况下,我使用了这个命令:mkdir "/Users/joao_parana/Documents/Shared Playground Data",并将我的文件README.md放在那里。

1

String.stringWithContentsOfFile已被弃用,不再与Xcode 6.1.1兼容。

创建您的documentDirectoryUrl。

let documentDirectoryUrl = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first! as NSURL

为确保文件位于那里,您可以使用Finder命令“前往文件夹”,然后将打印的documentDirectoryUrl.path复制并粘贴到那里。
println(documentDirectoryUrl.path!)
// should look like this: /Users/userName/Library/Containers/com.apple.dt.playground.stub.OSX.PLAYGROUNDFILENAME-5AF5B25D-D0D1-4B51-A297-00015EE97F13/Data/Documents

只需将文件名附加到文件夹URL作为路径组件。
let fileNameUrl = documentDirectoryUrl.URLByAppendingPathComponent("ReadMe.txt")
var fileOpenError:NSError?

在尝试打开文件之前,请检查文件是否存在。
if NSFileManager.defaultManager().fileExistsAtPath(fileNameUrl.path!) {

    if let fileContent = String(contentsOfURL: fileNameUrl, encoding: NSUTF8StringEncoding, error: &fileOpenError) {
        println(fileContent)        // prints ReadMe.txt contents if successful
    } else {
        if let fileOpenError = fileOpenError {
            println(fileOpenError)  // Error Domain=NSCocoaErrorDomain Code=XXX "The file “ReadMe.txt” couldn’t be opened because...."
        }
    }
} else {
    println("file not found")
}

0

Swift 5.7.1 - Xcode 14.1

func readFile() -> [String] {
    
    if let fileURL = Bundle.main.url(forResource: "File", withExtension: "txt") {
        do {
            let content = try String(contentsOf: fileURL)
            var x = content.components(separatedBy: "\n")
            x.removeAll { data in
                data.isEmpty
            }
            return x
        } catch {
            print(error)
        }
    }
    
    return [String]()
   
}
//Usage:
let input = readFile()

0
我无法在Playground中轻松地读取文件,最终只能在Xcode中创建一个命令行应用程序。这似乎非常适合我。

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