ファイル入出力

Emacs LispでCIIフォーマットをテキスト処理してみよう。以下、*scratch*バッファでC-j。

EmacsはOSの提供するファイルに対してバッファを割り当てており、
実際にはバッファに対して操作することになる。

  • ファイルを開く=バッファを割り当てる
(find-file "~/test.cii")

EmacsでC-x C-fしたのと一緒。

  • ファイルを開く=バックグラウンドでバッファを割り当てる
(find-file-noselect "~/test.cii")
#<buffer test.cii>
  • ファイルを例えば先頭から251バイト読む#1
(insert-file-contents "~/test.cii" nil 0 251)
("c:/meadow/test.cii" 251)
0C0111111111111222222222222333333333333444444444444555555555555666666666666777777777777888899900011--------------------------------------------------------------------------------------------------------------------------------------------------------

ん?この結果はなんだろう?

(setq x (insert-file-contents "~/test.cii" nil 1 251))
("c:/meadow/test.cii" 250)
C0111111111111222222222222333333333333444444444444555555555555666666666666777777777777888899900011--------------------------------------------------------------------------------------------------------------------------------------------------------

x
("c:/meadow/test.cii" 250)

どうも、insert-file-contents は、ファイル名とサイズのリストを返す際の
副作用として、結果をカレントバッファ(ここでは*scratchバッファ)に挿入するようだ。

うーん、これは、ファイルを読む+カレントバッファに挿入の2つの動作をしている。
ここでは、むしろ、カレントバッファに挿入した内容をまず、取得したいんだが…。

  • ファイルの先頭251バイトを読んで文字列を取得する
(setq f (find-file-noselect "~/test.cii"))
#<buffer test.cii>

(set-buffer f)
#<buffer test.cii>

(buffer-substring 0 251)

Debugger entered--Lisp error: (args-out-of-range 0 251)
  buffer-substring(0 251)
  eval ( (buffer-substring 0 251) )
  eval-last-sexp-1(t)
  eval-last-sexp(t)
  eval-print-last-sexp()
  call-interactively(eval-print-last-sexp)

あれエラーになった。qでデバッガから復帰。もういっちょ。

(buffer-substring 1 251)
#("(setq f (find-file-noselect  \"~/test2.cii\"))
#<buffer test2.cii>
(set-buffer f)
#<buffer test2.cii>
(buffer-substring 1 251)

ん?なんか、*scratch*バッファの内容をそのまま先頭からコピーしてるような…。
バッファの切り替えset-bufferと文字列の取得buffer-substringを一連の処理として
評価しないといかんのだな。

こうか?

(save-excursion
  (set-buffer f)
  (buffer-substring 1 251)
)
"0C0111111111111222222222222333333333333444444444444555555555555666666666666777777777777888899900011-------------------------------------------------------------------------------------------------------------------------------------------------------"

(setq str (save-excursion
   (set-buffer f)
   (buffer-substring 1 251)
))
"0C0111111111111222222222222333333333333444444444444555555555555666666666666777777777777888899900011-------------------------------------------------------------------------------------------------------------------------------------------------------"

str
"0C0111111111111222222222222333333333333444444444444555555555555666666666666777777777777888899900011-------------------------------------------------------------------------------------------------------------------------------------------------------"

OK、OK。

  • ファイルから251バイト単位でシーケンシャルに読む

関数を定義する。

(defun seq-read (f begin)
  (save-excursion
     (set-buffer f)
     (buffer-substring begin (+ begin 251))
  ))

しかし、いちいち、ファイルの読み出し位置を指定するのは、うまくない。

うーん…、ポイントを使えばいいようだ。
ファイルの終わりまで来た時は、nilを返してEOFとしよう。

(defun seq-read (f)
  (save-excursion
    (set-buffer f)
    (if (>= (point (point-max))
        nil
      (buffer-substring (point) (+ (point) 251))
  ))

(defun seq-succ (f)
  (save-excursion
    (set-buffer f)
    (if (>= (point) (point-max))
        nil
      (goto-char (+ (point) 251)) )
  ))

(seq-read f)
"0C0111111111111222222222222333333333333444444444444555555555555666666666666777777777777888899900011--------------------------------------------------------------------------------------------------------------------------------------------------------"

(seq-succ f)
252

(seq-read f)
"9D00001^@\371-----22222233444444444444555555555555666666666666777777777777888899900011-----------------------------------------------------------------------------------------------------------------------------------------------------------------------\376 "

うーん、seq-read と seq-succ は、構造がほとんど同じ上に別々に評価しないといけない。
seq-readで本当にやりたいことは、ファイルから読んだ文字列を返すと同時に、
読み込む位置を次にずらすことだ。

こういうときは、prog1を使うといいようだ。

これで、完成かな。ついでに、ポイントを巻き戻す関数も作っておこう。

(defun seq-read  (f)
  (save-excursion
    (set-buffer f)
    (if (>= (point) (point-max))
	nil
      (prog1 
	  (buffer-substring (point) (+ (point) 251))
	(goto-char (+ (point) 251))))
    ))

(defun seq-rewind (f)
  (save-excursion
    (set-buffer f)
    (goto-char (point-min))
  ))

(seq-read f)
"0C0111111111111222222222222333333333333444444444444555555555555666666666666777777777777888899900011--------------------------------------------------------------------------------------------------------------------------------------------------------"

(seq-read f)
"9D00001^@\371-----22222233444444444444555555555555666666666666777777777777888899900011-----------------------------------------------------------------------------------------------------------------------------------------------------------------------\376 "