できない.dev

ファイルに書き込むには

テキストファイルへ書き込む基本形を各言語で示す。
上書きと追記の違い、文字化けを避けるエンコーディング指定までを扱う。

公開: 更新:

選んだ言語は、ほかのサンプルコードを開いたときも最初に表示する。

Python

with open("a.txt", "w", encoding="utf-8") as f:
    f.write("こんにちは\n")

with 文を使うと書き込み後にファイルが自動で閉じられ、バッファのフラッシュ漏れを防げる。
モード "w" は既存内容を上書きするため、末尾に足したいときは "a" を使う。

JavaScript

import { writeFile } from "node:fs/promises";
await writeFile("a.txt", "こんにちは\n", "utf8");

Node.js の fs/promises を使う書き方で、ブラウザでは動かない。
writeFile は既存ファイルを上書きするため、追記したいときは appendFile を使う。

TypeScript

import { writeFile } from "node:fs/promises";
await writeFile("a.txt", "こんにちは\n", "utf8");

書き方は JavaScript と同じで、引数や戻り値の型は @types/node が供給する。
戻り値は Promise<void> なので await で完了を待つ。

Go

package main
 
import (
	"fmt"
	"log"
	"os"
)
 
func main() {
	// 上書き(無ければ作る)
	if err := os.WriteFile("a.txt", []byte("こんにちは\n"), 0644); err != nil {
		log.Fatal(err)
	}
 
	// 追記(無ければ作る)
	f, err := os.OpenFile("a.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatal(err)
	}
	if _, err := f.WriteString("追記\n"); err != nil {
		f.Close()
		log.Fatal(err)
	}
	if err := f.Close(); err != nil {
		log.Fatal(err)
	}
 
	data, err := os.ReadFile("a.txt")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Print(string(data))
	// -> こんにちは
	// -> 追記
}

os.WriteFile はファイルの作成・書き込み・クローズをまとめて行う。
第3引数はファイル作成時のパーミッションで、既存ファイルを上書きする場合は使われない。
追記するときは os.OpenFile に O_APPEND と O_WRONLY を渡し、ファイルが無いときに作らせるなら O_CREATE も加える。

Rust

use std::fs::{self, OpenOptions};
use std::io::Write;
 
fn main() -> std::io::Result<()> {
    // 上書き(無ければ作る)
    fs::write("a.txt", "こんにちは\n")?;
 
    // 追記(無ければ作る)
    let mut f = OpenOptions::new().append(true).create(true).open("a.txt")?;
    writeln!(f, "追記")?;
 
    print!("{}", fs::read_to_string("a.txt")?);
    // -> こんにちは
    // -> 追記
    Ok(())
}

fs::write はファイルを作成または切り詰めてから内容を書き込む。
追記は OpenOptions で append(true) を指定して開き、ファイルが無いときに作らせるなら create(true) も付ける。
? 演算子で Result のエラーを伝播するため、main の戻り値を std::io::Result<()> にしておく。

Java

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
 
public class Main {
    public static void main(String[] args) throws Exception {
        Path path = Path.of("a.txt");
        Files.writeString(path, "こんにちは\n"); // 上書き(無ければ作る)
        // 追記。APPEND だけだとファイルが無いときに NoSuchFileException になるので CREATE も渡す
        Files.writeString(path, "追記\n", StandardOpenOption.CREATE, StandardOpenOption.APPEND);
 
        System.out.print(Files.readString(path));
        // -> こんにちは
        // -> 追記
    }
}

Files.writeString は Java 11 以降で使え、オプションを渡さなければ UTF-8 で新規作成または上書きする。
オプションを渡すとこの既定の動作は適用されないので、StandardOpenOption.APPEND だけを渡すと、ファイルが無いときに NoSuchFileException になる。
追記では CREATE と APPEND を一緒に渡す。

C#

using System.IO;
using System.Text;
 
File.WriteAllText("a.txt", "こんにちは\n"); // 上書き(無ければ作る)
File.AppendAllText("a.txt", "追記\n");      // 追記(無ければ作る)
Console.Write(File.ReadAllText("a.txt"));
// -> こんにちは
// -> 追記
 
// Encoding.UTF8 を渡すと、先頭に BOM(EF BB BF)が付く
File.WriteAllText("b.txt", "こんにちは\n", Encoding.UTF8);
Console.WriteLine(BitConverter.ToString(File.ReadAllBytes("b.txt"), 0, 3)); // -> EF-BB-BF

File.WriteAllText はファイルの作成・書き込み・クローズをまとめて行い、既存ファイルは上書きする。
末尾に足したいときは File.AppendAllText を使い、ファイルが無ければ作られる。
エンコーディングを省略すると BOM なしの UTF-8 で書かれ、Encoding.UTF8 を渡すと、ファイルを先頭から書くときに BOM(EF BB BF の 3 バイト)が付くので、ほかのツールで読むファイルでは省略する。

つまずき

書き込みが反映されない場合は、モード("w" と "a")の取り違えや、with / await でクローズや完了を待てていないことを疑う。
相対パスは実行時のカレントディレクトリを基準に解決される点にも注意する。

追記するファイルが無いときの振る舞いは言語で違う

追記しようとしたファイルがまだ存在しないとき、Python の "a" モード、Node.js の appendFile、C# の File.AppendAllText は新しく作ってから書き込む。
一方で Go の os.OpenFile は O_CREATE、Rust の OpenOptions は create(true)、Java の Files.writeString は StandardOpenOption.CREATE を指定しないと作成せず、Go は no such file or directory、Rust は NotFound、Java は NoSuchFileException のエラーになる(いずれも本記事の更新時に Linux で確認した)。
ログのように初回だけファイルが無い用途では、サンプルのように作成の指定を必ず添える。

このコードで動いた?