【Go】os.OpenFileのフラグについて
※go version go1.19 darwin/amd64
ファイルを作成するGoの関数os.OpenFileは、3つの引数を取ります。
https://pkg.go.dev/os#OpenFile
func OpenFile(name string, flag int, perm FileMode) (*File, error)
第2引数のflag intは以下のような値を入れられます
os.OpenFile("filename.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
この縦棒でつないでいるやつなんなんだろうと思い、調べてみました。
第2引数に入れられる値は以下のようなintの数値が定義されていました。
const (
O_RDONLY int = syscall.O_RDONLY // open the file read-only.
O_WRONLY int = syscall.O_WRONLY // open the file write-only.
O_RDWR int = syscall.O_RDWR // open the file read-write.
O_APPEND int = syscall.O_APPEND // append data to the file when writing.
O_CREATE int = syscall.O_CREAT // create a new file if none exists.
O_EXCL int = syscall.O_EXCL // used with O_CREATE, file must not exist.
O_SYNC int = syscall.O_SYNC // open for synchronous I/O.
O_TRUNC int = syscall.O_TRUNC // truncate regular writable file when opened.
)
私の環境ではこのような値の数値でした
O_RDONLY int = 0
O_WRONLY int = 1
O_RDWR int = 2
O_APPEND int = 8
O_CREATE int = 512
O_EXCL int = 2048
O_SYNC int = 128
O_TRUNC int = 1024
「|」はORビット演算子らしく、intの値をORビット演算しているということでした!
各値を2進数で表すとこんな感じで、被り無く1がフラグのようになっているのがわかります(本来は64ビットなので、64桁になるように左に0が続く)
O_RDONLY int = 000000000000 // 0
O_WRONLY int = 000000000001 // 1
O_RDWR int = 000000000010 // 2
O_APPEND int = 000000001000 // 8
O_CREATE int = 001000000000 // 512
O_EXCL int = 100000000000 // 2048
O_SYNC int = 000010000000 // 128
O_TRUNC int = 010000000000 // 1024
すべての値をORビット演算子でつなぐと、「111010001011」こんな風な値になり、3723になるはず
package main
import (
"fmt"
"os"
)
func main() {
// すべてのフラグをORビット演算子でつなぐ
result := os.O_RDONLY | os.O_WRONLY | os.O_RDWR | os.O_APPEND | os.O_CREATE | os.O_EXCL | os.O_SYNC | os.O_TRUNC
fmt.Println(result)
}
▼実行結果
$ go run main.go
3723
playground:https://go.dev/play/p/D-p3-coCyNt
なる!
OpenFile関数も、フラグを「|」ではなくて「+」でつなげば動くんじゃないかな?
package main
import (
"io"
"os"
"strings"
)
func main() {
// 読み取り・書き込み、ファイルが存在しない場合は新規作成するのflagを足したものを引数に渡す
file, err := os.OpenFile("test.txt", os.O_RDWR+os.O_CREATE, 0666)
if err != nil {
panic(err)
}
defer file.Close()
io.Copy(file, strings.NewReader("テスト書き込み"))
// 読み込み位置を先頭に戻す
file.Seek(0, 0)
// ファイルの内容を標準出力に表示
io.Copy(os.Stdout, file)
}
▼実行結果
$ go run main.go
テスト書き込み
playground:https://go.dev/play/p/0jO4FaRNX1K
できてる!
おわりに
ふと気になったORビット演算子ですが、調べていくとGoのintの桁が環境によって桁数が違うことや、それがPCの64bit版・32bit版のことで、それがレジスタのことを表していて、bitをフラグとして扱ってシステムコールに渡して、OSからCPUに指示を出して…と、なんとなーく覚えたり忘れたりしていた知識がつながったのがとても楽しい経験でした、おわり!