Swift で Foundation を使わずにテキストファイルに書き込む。

Swift プログラミング

Swift は Foundation をインポートすれば String の writeTo メソッドを使ってテキストファイルに書き込めるようになります。

ただ Foundation に依存したくない場合もあるので、そんな時に C ライブラリを使って書き込む方法を調べてみました。


Swift は Foundation をインポートすると String 型に String(contentsOfFile:) writeToFile(path:, atomically:, encoding:) のようなテキストファイルに関する操作機能が追加されます。

普段はこれを使えば問題ないと思いますが、時には Foundation をインポートしたくない時もあると思うので、今回は Swift でも使える C 言語の標準ライブラリを使ってテキストファイルを操作してみることにしました。

C 言語のライブラリ使う準備

Swift で C 言語のライブラリ使うためには、OS X 環境であれば次のように Darwin.C フレームワークをインポートします。

import Darwin.C

ファイルハンドルを用意する

C 言語でファイル操作を行うときには、あらかじめ fopen関数 を使って ファイルを開いてファイルハンドルを取得する 操作が必要になります。

let path: String = "test.txt"
let mode: String = "w"

let handle: UnsafeMutablePointer<FILE> = fopen(path, mode)

fopen関数 の最初の引数には開きたいファイルのパスを文字列で指定します。そして次の引数ではファイルの開き方を指定します。今回のファイルの書き込みが目的なので、書き込み専用を意味する w を指定しています。

戻り値とエラー判定

ファイルハンドルは UnsafeMutablePointer<FILE>型 で得られます。ファイルハンドルの取得に失敗した場合は nil が返り、大域変数の errno に POSIX エラー番号が設定されます。

guard handle != nil else {
	
	fatalError("Failed to open a handle (errno: \(errno))")
}

ファイルハンドルを閉じる処理

また、ファイルハンドルは開いたら、使い終わったあとに fclose関数 を使って閉じる必要があるため、開き終わった次の行にでも defer を使って閉じる処理を書いておくと後が楽かもしれません。

ちなみに fclose関数 は、ファイルを正常に閉じられた時に 0 を返します。

defer {

	guard fclose(handle) == 0 else {
	
		fatalError("Failed to close the handle (errno: \(errno))")
	}
}

文字列をファイルに書き込む

ファイルハンドルが用意できたら fwrite関数 を使って文字列をハンドルが示すファイルに書き出します。

今回は UTF8 エンコードで文字列をテキストファイルに書きだそうと思うので、次のようにして文字列から UTF8 の配列を作って、そのバッファーポインタのアドレスとサイズを fwrite関数 に渡して書き込み処理を行います。

この時 fwrite関数 は戻り値として 書き込んだデータのユニット数 を返すようになっていますが、書き込みに失敗した場合には一個人に指定した書き込み数よりも少ない方が返るようになっているので、それを見て書き込みが成功したかを判断しています。

let units = Array(string.utf8)

units.withUnsafeBufferPointer { (buffer: UnsafeBufferPointer<UTF8Char>) in
			
	let unitSize = sizeof(UTF8Char.self)
	let unitCount = buffer.count
			
	guard fwrite(buffer.baseAddress, unitSize, unitCount, handle.rawValue) == unitCount else {
				
		fatalError("An error occurred during writing to the handle (errno: \(errno))")
	}
}

これで文字列をファイルに書き込むことができました。