%=====================================================================
% 秋田高専 5E 計算機応用 テキスト
%テーマ 練習(5) 関数
%    本テキストの内容
%        ・ファイル処理関数
%          ・
%          ・
%          ・
%          ・
%          ・
%          ・
%          ・
%
% last updated 2005.7.4
%    created by  Masashi Yamamoto
%     e-mail yamamoto@akita-nct.jp
%=====================================================================
\documentclass[10pt,a4paper]{jarticle}
\usepackage{graphicx,amsmath,amssymb,ascmac,theorem,float}
\usepackage{listings, jlisting, html}
\oddsidemargin 0mm  %左の余白 25.4mm-0mm奇数ページ
\evensidemargin 0mm %左の余白 25.4mm-0mm偶数ページ
\textwidth 160mm
\newcommand{\command}[1]{「\texttt{#1}」}
\newcommand{\tw}[1]{\texttt{#1}}
\newcommand{\vm}[1]{\boldsymbol{#1}}
\newcommand{\diff}[0]{\mathrm{d}}
\newcommand{\pdiff}[3]{
 \if 1#1 \frac{\partial #2}{\partial #3}
 \else \frac{\partial^{#1} #2}{\partial #3^{#1}}\fi
}
%
\renewcommand{\lstlistingname}{リスト}
\lstset{language=C,%
        basicstyle=\footnotesize,%
        commentstyle=\texttt,%
        classoffset=1,%
        keywordstyle=\bfseries,%
	frame=tRBl,framesep=5pt,%
	showstringspaces=false,%
        numbers=left,stepnumber=1,numberstyle=\footnotesize%
	}%
%
\begin{htmlonly}
\usepackage{verbatimfiles}
 \providecommand{\lstinputlisting}[2][]{\verbatimlisting{#2}}
\end{htmlonly}
%
\begin{document}
\title{C言語の学習\\ファイル処理関数}
\author{山本昌志\thanks{独立行政法人　秋田工業高等専門学校　電気工学科}}
\date{2004年7月7日}
\maketitle
%
%=====================================================================
\section{本日の学習内容}
%=====================================================================
本日の内容は、教科書の18章ファイル処理関数に付いて学習する。
%
\begin{list}{}{\leftmargin=5zw \rightmargin=2zw \labelsep=1zw
 \labelwidth=5zw \itemindent=0pt}
 \item[\textbf{18章}]\textbf{ファイル処理関数}
\end{list}
%

本講義のメインテーマである数値計算では、大量の数値を扱うことが多い。いちいち紙に
書き写すことは不可能なので、ハードディスクに保存されるのが普通である。数値計算に
限らず、現代の実験では、データの取得にはコンピューターが使われ、そのハードディス
クに大量のデータが保存される。そのデータを処理して、意味のある量に加工するのであ
る。このようなことから、ファイル処理に関わるテクニックはコンピューターを使う上で
必須となっている。ファイル処理の技術を習得し、コンピューターを自在に活用して欲し
い。
%
%=====================================================================
\section{ファイル処理関数(18章)}
%=====================================================================
%---------------------------------------------------------------------
\subsection{ファイル処理の体験}
%---------------------------------------------------------------------
%--------------------------
\subsubsection{ファイル出力}
%--------------------------
ごちゃごちゃとファイル処理について、説明する前に、実際にファイル処理を体験してみ
よう。

ファイル出力の例として、ディスプレイに出力していた"Hello World !!"をファイルに書
き出す。リスト\ref{prog:hello}のプログラムを実行させて、作成されたファイル
(hello.txt)をエディター(emacsなど)で内容を確認しよう。
%
\begin{list}{}{\leftmargin=7em \labelwidth=5em}
 \item[4行]ファイルポインターの宣言。
 \item[6行]ファイルのオープン
 \item[8行]ファイルへの書き込み
 \item[10行]ファイルのクローズ
\end{list}
%
\lstinputlisting[caption=ファイル出力の例,
label=prog:hello]{program/hello.c}
%
%--------------------------
\subsubsection{ファイル入力}
%--------------------------
先ほど作成したファイル(hello.txt)の内容を読み、それを画面に出力してみよう。
%
\begin{list}{}{\leftmargin=7em \labelwidth=5em}
 \item[5行]ファイルポインターの宣言。
 \item[8行]ファイルのオープン
 \item[10行]ファイルからデータの読み込み
 \item[14行]ファイルのクローズ
\end{list}
%
\lstinputlisting[caption=ファイル入力の例,
label=prog:read]{program/read.c}
%
%---------------------------------------------------------------------
\subsection{数値計算のファイル入出力}
%---------------------------------------------------------------------
C言語のファイル入出力は、用途に応じた処理ができるように、様々な機能が用意されて
いる。しかし、数値計算で使う場合、以下のようにすれば良い。この辺りについては、す
ぐに理解できなくても良いが、ファイル処理を多用するプログラムを作成する場合には各
自勉強して欲しい。ただ、教科書にはいろいろ書かれているので、諸君がこの講義で必要
なファイル処理の機能をまとめているだけである。
%
\begin{itemize}
 \item 高水準ファイル処理を使え
       \begin{quote}
	教科書に書いてあるとおり、高水準ファイル処理と低水準ファイル処理が、通常
	は前者を使う。前者はバッファーを文字や数値単位、あるいは文字単位で処理す
	る関数が用意されており、容易にプログラムができる。それに対して、後者はバ
	イト単位で処理するので、細かい処理ができるが扱いは面倒である。
       \end{quote}
 \item テキストモードを使え
       \begin{quote}
	改行コードの処理により、テキストモードとバイナリーモードを使い分ける。
	Linux(UNIX)ではC言語と同じ改行コードを使う。したがって、テキストモードも
	バイナリーモードも同じ結果が得られる。Windowsの場合は、テキストモードだ
	と改行コードの変換が行われるので、注意が必要である。この辺の所は教科書を
	見て欲しい。
       \end{quote}
 \item シーケンシャルファイル処理を使え
       \begin{quote}
	ファイルのデータへのアクセス方法には、シーケンシャルファイル処理とランダ
	ムファイル処理がある。前者はデータを先頭から順にアクセスし、後者は任意の
	場所からアクセスできる。本講義ではランダムアクセル処理を使うことはない。
       \end{quote}
 \item ファイル出力には、\tw{fprintf()}関数を使え
       \begin{quote}
       \tw{fprintf()}関数は、標準出力(ディスプレイ)と同じ使い方ができる。画面に
       出力するのと同じイメージでファイルに書き込むことができるので、容易にプロ
       グラムが書ける。実際、出力したファイルをemacsのようなエディターで見ると画
       面出力と同じである。
       \end{quote}
 \item ファイル入力には、\tw{fscanf()}関数を使え
       \begin{quote}
       \tw{fscanf()}関数は、標準入力(キーボード)と同じ使い方ができる。キーボード
	からデータを入力するのと同じイメージでファイルからデータを読み込むことが
	できる。諸君は、その方法になれているので、容易にプログラムが書けるだろう。
       \end{quote}
\end{itemize}
%
%---------------------------------------------------------------------
\subsection{ファイル処理の流れと関連事項}
%---------------------------------------------------------------------
%------------------------
\subsubsection{処理の流れ}
%------------------------
ほとんどのプログラム言語では、ファイルの処理は図\ref{fig:flow_file_manage}のよう
になっている。これは人間が、データを記録している本やノートなどを見る動作と全く同
じようになっている。\textbf{オープン}と\textbf{読み書き}、\textbf{クローズ}は約
束事と理解して欲しい。また、同時に複数個のファイルをオープンすることも可能である。
%
\begin{figure}[H]
 \begin{center}
  \includegraphics[keepaspectratio, scale=1.0]
  {figure/flow_file_manage.eps}
  \caption{コンピューターのファイル処理と人間の読み書き方法}
  \label{fig:flow_file_manage}
 \end{center}
\end{figure}
%

リスト\ref{prog:hello}や\ref{prog:read}のプログラムの中で、これらの約束事の記述
の例を図\ref{fig:flow_file_manage_ex}に示す。人間の動作を考えれば、取
り立ててその流れは難しくない。
\begin{itemize}
 \item \tw{fopen()}関数でファイルをオープンしている。
 \item \tw{fprintf()}や\tw{fscanf()}関数で、ファイルのデータを読み書きしている。
 \item \tw{fclose()}関数で、ファイルをクローズしている。
\end{itemize}
%
\begin{figure}[H]
 \begin{center}
  \includegraphics[keepaspectratio, scale=1.0]
  {figure/flow_file_manage_ex.eps}
  \caption{C言語でのファイル処理の例}
  \label{fig:flow_file_manage_ex}
 \end{center}
\end{figure}
%

図\ref{fig:flow_file_manage_ex}を見て分かるように、実際のC言語では\textbf{オープン} 
と\textbf{読み書き}、\textbf{クローズ}の他に、ファイルポインターの宣言が必要であ
る。ファイルポインターについては、次の節で述べることにする。C言語のプログラムで
ファイル処理をする場合は、図\ref{fig:flow_file_manage_C}に示す手順に従えば良い。
ただし、実際のプログラムではエラー処理を書かなくてはならないが、ここでは示してい
ない。
%
\begin{figure}[hbtp]
 \begin{center}
  \includegraphics[keepaspectratio, scale=1.0]
  {figure/flow_file_manage_C.eps}
  \caption{C言語でのファイル処理の流れ}
  \label{fig:flow_file_manage_C}
 \end{center}
\end{figure}
%
%------------------------------
\subsubsection{オープンとクローズ}
%------------------------------
C言語では、かなり細かいファイルの処理ができる。そのために、ファイルの情報をメモ
リーの一部に格納する必要がある。その格納場所を示すものがファイルポインターである。
それは構造体になっており、\tw{stdio.h}というヘッダーファイルにその内容が定義され
ている。定義内容の例が教科書(p.377)に書かれている。以下のようなことが記述されて
いる。
\begin{itemize}
 \item ファイルの読み書きをする場合の位置
 \item 残っている文字数
 \item バッファー領域の先頭位置
 \item ファイルの状態
 \item ファイル番号
\end{itemize}

すべてこのファイルポインター(\tw{fp})を使って、ファイル関係の処理は実施する事になる。なに
せ、ファイルに関する情報が全て書かれているので、これを指定すれば、あとはコン
ピューターが勝手に処理してくれる。面倒くさい処理はコンピューター任せにして、プロ
グラマーは楽をしようということである。この\tw{FILE}型の変数(ポインター)を使うた
めには、次のように宣言する。
%
\begin{quote}
 \begin{verbatim}
FILE *fp;
 \end{verbatim}
\end{quote}
%
FILE型の変数(ポインター)\tw{fp}を宣言したのである。ただし、\tw{fp}は変数名なので
プログラマーが勝手な名前をつけて良い。

通常は、\tw{fopen()}という関数の戻り値をこのポインターに代入する。このファイルを
オープンする関数\tw{fopen()}の書式は、次の通りである。
%
\begin{quote}
     \tw{FILE *fopen(char *}\textit{filename}\tw{, char *}\textit{openmode}\tw{)}
\end{quote}
%    
戻り値はFILE型のポインター、ファイル名を表す第一引数はchar型のポインター、オープ
ンモードを表す第2引数はchar型のポインターと言うことである。もし、オープンに失敗
すると、NULLという戻り値になります。char型のポインターと難しいことを言っているが、
先のプログラムの例(リスト\ref{prog:hello}, \ref{prog:read})でも分かるように、文
字列をダブルクォーテーションで囲めば良いのである。例えば、hoge.txtというファイル
を読み込みモードでオープンする場合
%
\begin{quote}
 \begin{verbatim}
fp=fopen("hoge.txt", "r");
 \end{verbatim}
\end{quote}
%
と書けば良い。

オープンモードについては、いろいろ用意されており、教科書のp.382にまとめてある。
細かいファイル処理をする場合は、これらのモードを巧みに使う必要があるが、本講義で
は、
\begin{itemize}
 \item ファイルからデータを読み込む場合は、オープンモードの部分は\tw{"r"}
 \item ファイルへデータを書き込む場合は、オープンモードの部分は\tw{"w"}
\end{itemize}
とすればよい。バイナリーモードもUNIX(Linux)では関係ないので使わない。

ファイルをクローズする関数\tw{fclose()}の書式は、簡単で、次の通りである。
\begin{quote}
\tw{int fclose(FILE *}\textit{filepointer}\tw{)}
\end{quote}
戻り値は、int型で、クローズに成功すると0、失敗するとEOFが返される。引数は、ファ
イルポインターのみである。ファイルを開いたら閉じるのが礼儀だと心得て、処理の最後
に書きましょう。
%
%-------------------------------
\subsubsection{ファイル入出力関数}
%-------------------------------
いよいよ、ファイルのデータを読み書きするファイル入出力関数について説明する。難し
そうですが、実は非常に簡単である。いままで、標準入力(キーボード)と標準出力(ディ
スプレイ)に使ってきた関数、\tw{printf()}と\tw{scanf()}とほとんど同じである。
付録に示すようにキーボードやディスプレイもファイルとして取り扱われるので、同じ手
法がハードディスクにも使える。

まず、入力からですが、一般のファイルと標準入力の場合を並べて書くと
\begin{quote}
 \begin{tabular}{p{25mm}l}
  \textbf{ファイル入力} & \tw{int fscanf(}\textit{ファイルポインター}\tw{,}\textit{書式指定}\tw{,}\textit{引数並び}\tw{)} \\
  \textbf{標準入力} & \tw{int scanf(}\textit{書式指定}\tw{,}\textit{引数並び}\tw{)}
 \end{tabular}
\end{quote}
となる。ファイルポインターを指定する以外、すべて標準入力の場合と同じである。非常
に単純で簡単である。実際の動作もキーボードからデータを入力するのも、ファイルから
読み込むのも同じイメージで取り扱える。

出力もまったく同じである。
\begin{quote}
 \begin{tabular}{p{25mm}l}
  \textbf{ファイル出力} & \tw{int fprintf(}\textit{ファイルポインター}
  \tw{,}\textit{書式指定}\tw{,}\textit{引数並び}\tw{)} \\
  \textbf{標準出力} & \tw{int printf(}\textit{書式指定}\tw{,}\textit{引数並び}\tw{)}
 \end{tabular}
\end{quote}
ハードディスクのファイルにデータを書き込むのは、ディスプレイにデータを出力するのと全く同
じイメージである。実際、ファイル出力されたデータを見ると、ディスプレイと同じであ
ることが分かる。

これで、コンソール入出力をしつこく詳細に説明した理由がわかったでしょう。コンソー
ル入出力とファイル入出力は同じ取り扱いができるのである。
%
%---------------------------------------------------------------------
\subsection{ファイル出力の実際}
%---------------------------------------------------------------------
計算結果などを大量のデータはハードディスクに保存しなくてはならない。ファイル出力のコツは、
\begin{itemize}
 \item 表をイメージして、データをファイルに書き出す。
 \item 1行に複数のデータがある場合は、"\textbackslash t"を用いてタブ区切り
       \footnote{データの区切りとして、タブは良く使われる。ファイルの内容ををエ
       ディターで見るときに、データの並びがそろっているため、視認性がよくなるか
       らである。}とする。
 \item ループ文(\tw{for, while, do while})を用いて、\tw{fprintf()}関数を繰り返し
       使う。
\end{itemize}
である。

リスト\ref{prog:file_out}に三角関数の値をファイル出力するプログラムを示す。この
プログラムを実行して、作成されたファイルを適当なエディター(emacs等)で見よ。
%
\lstinputlisting[caption=三角関数の値のファイル出力プログラム,
label=prog:file_out]{program/file_out.c}
%
%---------------------------------------------------------------------
\subsection{ファイル入力の実際}
%---------------------------------------------------------------------
数値計算では、処理の対象となる大量のデータをハードディスクから読み込むことが多い。
数値計算に限らず、現代の実験ではコンピューターのハードディスクにデータが蓄えられ
るのは普通である。ハードディスクに保存されたデータを読み出し、それを処理すること
が実験のデータ整理に必要となる。このような場合、ファイルからのデータ入力のコツは、
%
\begin{itemize}
\item データ数が多い場合、読み込んだデータは配列(あるいは構造体)に格納する。
\item ループ文を用いて、\tw{fscanf()}関数を繰り返し使い、データを読み込む。
\item \tw{fscanf()}関数の戻り値が\tw{EOF}の場合\footnote{\tw{EOF}はend of fileの略でファイルの終わりを示す。}、データの読み込みを止める。
\end{itemize}
%       
である。

リスト\ref{prog:file_in}に先ほど作成したファイル内容を読み込み、ディスプレイに出
力するプログラムを示す。このプログラムを実行して、ファイルの読み込みの練習をせよ。
%
\lstinputlisting[caption=ファイルの内容を読み込むプログラム,
label=prog:file_in]{program/file_in.c}
%
%=====================================================================
\section{付録}
%=====================================================================
%----------------------------------------------------------
\subsection{特別なファイル(標準入力、標準出力、標準エラー出力)}
%----------------------------------------------------------
C言語でファイルを取り扱う場合、以下のようにプログラムを作成しなくてはならない。
\begin{enumerate}
 \item ファイルポインター用の変数をFILE型で宣言する。
 \item ファイルをオープンする。
 \item ファイルの読み書き
 \item ファイルのクローズ
\end{enumerate}
しかし、特別な3個のファイル(標準入力、標準出力、標準エラー出力)は、いきなりファ
イルの読み書きができる。コンソール入力で述べたように、通常、標準入力はキーボード、
標準出力はディスプレイを示す。C言語では(UNIXでは)、キーボードやディスプレイもファ
イルとして扱われ、読み書きする。それどころか、すべてのデバイスがファイルとして扱
われる。そうすると、シンプルな取り扱いが可能となる。

これら、特別な3個のファイルについて、表\ref{table:std_in_out_file}にまとめる。
\begin{table}[H]
 \caption{標準入出力ファイル}
 \label{table:std_in_out_file}
 \begin{center}
  \begin{tabular}{lll}
   \hline
   ファイル & ファイルポインター & デバイス(通常) \\
   \hline \hline
   標準入力 & \tw{stdin} & キーボード \\
   標準出力 & \tw{stdout} & ディスプレイ \\
   標準エラー出力 & \tw{stderr} & ディスプレイ\\
   \hline
  \end{tabular}
 \end{center}
\end{table}
%

\tw{fscanf()}関数でファイルポインターとして\tw{stdin}を指定した場合、
\tw{scanf()}と同じ動作をする。一方、\tw{fprintf()}関数でファイルポインターとして
\tw{stdout}を指定した場合、\tw{printf()}と同じ動作をする。これを上手に使うと、プ
ログラムのデバッグのときに便利である。

最後に標準エラー出力について述べる。標準エラー出力とは、エラーが発生した場合のメッ
セージなどを出力先のことを言う。プログラム中で処理にエラーが発生した場合、そのメッ
セージの出力先に指定する。\tw{printf()}関数を使うよりも、\tw{fprintf()}関数でファ
イルポインターとして\tw{stderr}を指定した場合、\tw{printf}関数と同じ動作をする。

\begin{quote}
 \begin{verbatim}
fprintf(stderr, "ファイルの読み込みに失敗しました\n");
 \end{verbatim}
\end{quote}

こうするとエラーメッセージのみ、リダイレクトすることができプログラムの保守性が上
がります。本当は、教科書p.309に書かれている\tw{perror()}関数を使うのがもっとも良
いだろう。
%======= 参考文献 =====================================================
\bibliographystyle{junsrt}
\bibliography{reference} 
%=====================================================================
\end{document}
