はじめに
ファイルパス名が指定されたら、指定されたファイルからテキストを入力、
ファイルパス名が省略されたら、標準入力からテキストを入力する。
このように動作する テキスト入力関数 を作成しました。
テキストファイル入力 標準入力 共通関数
func readTextFile(filepath: String?) throws -> [String] {
var lines = [String]()
if let filepath {
let contents = try String(contentsOfFile: filepath, encoding: .utf8)
contents.enumerateLines { line, _ in lines.append(line) }
} else {
while let input = readLine() {
lines.append(input)
}
}
return lines
}
String(contentsOfFile:encoding:)
が一括入力のため、標準入力もEOFまで先に入力します。このため、巨大ファイルサイズには不向きです。
使い方
上記関数を呼び出す例
test.swift
import Foundation
let test: () = {
let arguments = CommandLine.arguments
let filename: String? = arguments.count > 1 ? arguments[1] : nil
do {
let text = try readTextFile(filepath: filename)
print(text)
} catch {
print(error.localizedDescription)
}
}()
実行結果
% swiftc test.swift
% ./test a.txt # filepath
["123", "ABC"]
% ./test <a.txt # stdin
["123", "ABC"]
% cat a.txt
123
ABC
実行結果(エラー)
% cat b.txt
cat: b.txt: No such file or directory
% ./test b.txt #filepath
The file “b.txt” couldn’t be opened because there is no such file.
% ./test <b.txt #stdin
zsh: no such file or directory: b.txt
イテレータ版
ついでに、イテレータ版も作成しました。
イテレータ版
struct readTextFile: Sequence, IteratorProtocol, Sendable {
@usableFromInline
internal var array: [String]? = nil
@usableFromInline
internal var index = 0
init(filepath: String?) throws {
if let filepath {
let contents = try String(contentsOfFile: filepath, encoding: .utf8)
var array = [String]()
contents.enumerateLines { line, _ in array.append(line) }
self.array = array
} else {
array = []
while let input = readLine() {
array!.append(input)
}
}
}
@inlinable
public mutating func next() -> String? {
if let array {
if index < array.count {
defer { index += 1 }
return array[index]
}
}
return nil
}
}
使い方
上記イテレータ版を呼び出す例
test.swift
import Foundation
let test: () = {
let arguments = CommandLine.arguments
let filename: String? = arguments.count > 1 ? arguments[1] : nil
do {
for line in try readTextFile(filepath: filename) {
print(line)
}
} catch {
print(error.localizedDescription)
}
}()
実行結果
% swiftc test.swift
% ./test a.txt # filepath
123
ABC
% ./test <a.txt # stdin
123
ABC
追伸
stdin
を再割り当てする方法がもっと簡単でした。次の記事にします。
以上