std.stdioとは何か?D言語における標準入出力モジュールの概要と基本機能を詳細にわかりやすく解説

目次

std.stdioとは何か?D言語における標準入出力モジュールの概要と基本機能を詳細にわかりやすく解説

std.stdioモジュールは、D言語における標準的な入出力操作を提供する中核的なライブラリです。コンソールへの表示やキーボードからの入力、ファイルの読み書きなど、基本的なI/O機能を簡潔に実現できるよう設計されています。本セクションでは、std.stdioの役割や特徴、提供する主な機能について概観します。

std.stdioモジュールの概要と役割:D言語における標準入出力の基盤となる仕組みと重要性を詳しく解説

std.stdioはD言語公式の標準ライブラリPhobosに含まれるモジュールの一つで、標準入出力(Standard I/O)機能を提供します。その役割は、プログラムからコンソールファイルへのデータの出力、およびそれらからの入力を簡単かつ安全に行えるようにすることです。このモジュールを利用することで、複雑な低レベルのシステムコールやC言語のstdioライブラリを直接扱うことなく、D言語らしい高レベルなコードで基本的な入出力操作を実現できます。

標準入出力におけるstd.stdioの特徴:C言語のstdioとの違いと独自の機能

D言語のstd.stdioは内部的にはC言語のstdio(stdio.h)の機能を活用していますが、D独自の型安全性例外処理の仕組みを備えている点が特徴です。例えば、C言語ではprintfやscanfのように書式指定文字列に頼る入出力を行いますが、std.stdioではテンプレートを用いた関数(writefreadfなど)が提供され、コンパイル時に型チェックが行われるため、誤った型のデータを出力・入力しようとするとコンパイルエラーになります。また、ファイル操作においてもC言語のFILE*ポインタを直接扱うのではなく、D言語ではFile構造体によって安全にラップしており、自動でファイルを閉じる仕組み(RAII)を実現しています。これらにより、従来のC言語の標準入出力に比べ、D言語のstd.stdioはより安全で高機能なI/Oを提供しています。

std.stdioで提供される主な関数と型:便利なI/O操作を実現する機能一覧

主要な関数として、標準出力向けにはwrite, writeln(改行あり)、書式付きのwritef, writeflnなどがあり、標準入力向けにはreadln(1行読み取り)やreadf(フォーマット指定読み取り)が用意されています。また、ファイル操作のためのFile構造体と、そのインスタンスに対するwrite/writeln/readfメソッド、さらに標準ストリームオブジェクト(stdin, stdout, stderr)もstd.stdioで提供されます。加えて、入力を行単位で処理するlinesや、一定サイズのチャンクで読み込むchunksといった便利な範囲 (range) ベースのユーティリティも含まれています。

std.stdioモジュールの導入方法:import宣言と名前空間の使い方

D言語でstd.stdioを利用するには、プログラムの先頭でimport std.stdio;と宣言するだけです。これによりstd.stdio内の関数や型(writewritelnFile構造体など)が利用可能になります。特定の関数だけをインポートしたい場合は、import std.stdio : writeln;のようにコロン (:) の後に名前を指定することで限定的にインポートすることもできます。ただし、基本的には全体をインポートして問題ありません。std.stdioは標準ライブラリの一部なので追加のインストールは不要で、D言語のコンパイラ(dmdなど)で標準的に利用できます。

std.stdioの内部構造と動作原理:3層構造によるクロスプラットフォームなI/O実装

std.stdioの内部実装を見てみると、入出力処理は大きく3層構造になっています。最下層はOSのシステムコール(WindowsならWriteFile/ReadFile、Linuxならread/writeなど)で、その上にC言語のstdio(stdio.h)がOS差を吸収する形で存在し、最上位にD言語のstd.stdioが位置します【標準入出力の3層構造】。std.stdioはCのstdioをpublic import(公開インポート)することで再利用しつつ、D言語独自の安全機構や高水準インターフェイスを提供しています。この構造により、プログラマはプラットフォーム固有の違いを意識せずに同一のコードで入出力を扱えます。またstd.stdio自体も、Cのstdio関数を直接ラップするだけでなくDらしく拡張しているため、使い勝手と移植性の両立が図られています。

D言語でのHello, World:writeln関数を使った基本的な標準出力プログラムの書き方ガイド

まずは、std.stdioを使った最も基本的なプログラムとして「Hello, World」の表示を行ってみましょう。D言語で標準出力に文字列を出力する手順や、プログラム全体の構成を確認します。

D言語のHello, Worldプログラム:import宣言とmain関数を含む基本構成を解説

import std.stdio;
void main() { writeln("Hello, World"); } 

上記のコードはD言語における典型的なHello, Worldプログラムです。import std.stdio;で標準入出力モジュールを読み込み、void main()関数内でwriteln("Hello, World");を呼び出しています。main関数はプログラムのエントリーポイントであり、writeln関数によって指定した文字列が標準出力に表示されます。このプログラムをコンパイル・実行すると、コンソールに「Hello, World」と出力されることを確認できるでしょう。

writeln関数の基本構文:文字列リテラルを標準出力に表示する方法

writeln関数は、与えられた引数を文字列に変換して標準出力に表示し、最後に改行も付加してくれる便利な出力関数です。例えば文字列リテラル"Hello, World"を渡せば、その文字列がそのまま画面に表示されます。フォーマット指定子を使わずにシンプルに文字列を出力できる点が特徴で、改行コード (\n) も自動で挿入されるため、別途改行を入れる必要もありません。writelnは任意の型の引数を複数取ることができ、それぞれを順に出力します(詳細は後述します)。まずは文字列を一つ出力する基本形として上記のように使用します。

Hello, Worldプログラムのコンパイルと実行方法:コンソール出力結果の確認手順

D言語のソースコードを書いたら、Dコンパイラ(例えばdmd)でコンパイルして実行ファイルを作成します。先ほどのHello, Worldプログラムをファイルに保存し、コマンドラインでdmd hello.dとコンパイルすると、hello(Windowsではhello.exe)という実行ファイルが生成されます。それを実行することで、コンソール上にHello, Worldという出力が得られるはずです。標準出力へ文字列が正しく表示されれば、std.stdioおよびwriteln関数が期待通り動作していることの確認になります。開発環境によっては、エディタやIDEから直接ビルド・実行もできますが、いずれの場合もコンソールに文字列が表示されることを確認してみましょう。

改行とエンコーディング:writelnが自動で改行する仕組みとUnicode文字の表示対応

ここで、writelnが自動的に挿入する改行について補足します。writelnはプラットフォームに応じて適切な改行を出力します。例えば、Unix/LinuxやmacOSでは"\n"(LF)のみ、Windowsでは"\r\n"(CR+LF)の改行シーケンスが出力されます。プログラマが意識して切り替える必要はなく、writelnに任せておけば環境に応じた改行が行われます。また、D言語の文字列はUTF-8エンコーディングのユニコード文字列であるため、日本語を含むマルチバイト文字もwritelnでそのまま正しく出力できます。ただし、実行環境のコンソールがUTF-8に対応している必要があります(多くの環境ではデフォルトで対応しています)。このように、writelnは国際化された文字列や改行処理にも十分配慮された設計になっています。

変数や式の出力:writelnでメッセージに動的データを組み込む方法を解説

静的な文字列だけでなく、変数の値や計算結果を出力したい場合も、writelnは簡単に利用できます。例えば、整数変数aの値を表示するには、writeln("値は", a, "です");のように複数の引数を渡します。すると、文字列"値は"、変数aの中身、文字列"です"が順に連結されて出力されます。このように,で区切って複数の要素をwritelnに渡せば、自動的に連続して表示されるため、C言語のprintfのように%dを書いたり文字列連結演算子を使ったりする必要はありません。また、計算式を直接writelnに渡すことも可能です(例:writeln("3+5=", 3+5)3+5=8と出力)。このように、writelnを使うことで動的なデータを含むメッセージも簡潔に表示できます。

D言語標準出力関数write・writeln・writef・writeflnの違いと正しい使い方を徹底解説

標準出力に文字を表示する関数には、write, writeln, writef, writeflnの4種類があります。それぞれ改行の有無やフォーマット指定の対応といった違いがあり、用途に応じて使い分ける必要があります。本セクションでは、それぞれの関数の違いと使い方を具体的に見ていきましょう。

writeとwritelnの違い:出力後に改行するかどうかの挙動の比較

write("Hello, "); write("World"); writeln("!"); writeln("Next line"); 

write関数は出力後に改行をしないのに対し、writeln関数は出力内容の後に自動的に改行を行います。例えば上のコードを実行すると、コンソールにはHello, World!と表示され、その後に改行されます。つまり、writeは文字列をそのまま出力するだけで改行しないため、次の出力が同じ行に続きます。一方、writelnは出力内容の末尾で改行するため、その次にwritelnwriteを呼び出した場合は新しい行から出力が始まります。改行の有無以外の挙動(引数の扱いや文字列変換など)はwritewritelnで同じです。画面にプロンプトを表示してユーザー入力を待つ場合など、改行したくないケースではwriteを使い、通常は一度の出力で行を完結させるためwritelnを使うといった具合に使い分けます。

writefとwriteflnの違い:フォーマット文字列による出力と改行処理の比較

writefwriteflnは、C言語のprintfに相当する書式付き出力を行う関数です。違いは、こちらも末尾に改行を付加するかどうかだけです。writef(fmt, args...)は指定したフォーマットfmtに従って出力しますが、自動改行はしません。一方、writeflnwritefの動作に加えて出力後に改行を入れます。例えば、writef("Hello %s", "World"); writefln("! %d", 2023);というコードを実行すると、コンソールにはHello World! 2023と表示され、その後ろで改行されます(writeflnの効果)。逆に、全てwritefで書く場合はフォーマット文字列内に"\n"を入れるか、最後に明示的にwritelnを呼ぶ必要があります。フォーマット指定したい場合にはまずwritefを使い、必要に応じて改行付きのwriteflnを選ぶという点で、基本的な使い分けはwrite/writelnと同様です。

書式指定出力とは何か:writef/writeflnで利用できるフォーマット指定子の使い方

int x = 5; double y = 2.5; writefln("%dと%.1fを足すと%.1fになります", x, y, x+y); 

上記のように、writef/writeflnではC言語と同様にフォーマット文字列の中に%から始まる書式指定子を埋め込み、後に続けてその分の引数を渡します。例えば%dは整数、%sは文字列、%fは浮動小数点(%.1fのように書けば小数点以下1桁まで表示)といった具合です。writefln("%dと%.1fを足すと%.1fになります", x, y, x+y);というコードでは、整数変数xと浮動小数点変数yの値が指定の書式で埋め込まれ、出力結果は5と2.5を足すと7.5になりますとなります。書式指定子と引数の数・型が合わない場合、コンパイルエラーとなるため、C言語のprintfより安全に利用できます。フォーマット指定によって出力を整形したい場合には、これらのwritef/writefln関数を活用するとよいでしょう。

複数引数の出力方法:write/writelnで複数の値を連続して表示するテクニック

D言語の出力関数は可変個の引数を取れるため、一度の呼び出しで複数の値を続けて表示できます。これは前述のように,で区切って複数の引数をwrite/writelnに渡すだけです。それぞれの引数は自動的に文字列に変換され(配列やオブジェクトなら適切な形式にtoStringされ)順に出力されます。ただし、引数同士の間に自動的な空白や区切り文字は挿入されない点に注意が必要です。例えば、writeln(1, 2, 3)とすると123と連続して表示されます。見やすい出力にしたい場合は、writeln(1, ", ", 2, ", ", 3)のように明示的に区切り文字やスペースを入れるか、writef"%d, %d, %d"と指定するなど工夫します。複数の値を一度に出力できる柔軟性は、D言語の出力関数の使い勝手を高めるポイントの一つです。

出力関数の適材適所:write系とwritef系を使い分けるシーンと注意点を解説

以上のような違いを踏まえて、出力関数は状況に応じて適切に選択することが重要です。基本的には、単純に値を表示する場合は手軽なwritelnを使い、改行したくない場合にwriteを使います。書式指定が必要な場合にはwritef/writeflnを使います。また、ユーザーからの入力を促すプロンプトを表示する際など、改行せずに出力してそのまま入力待ちにしたいケースではwriteを使いますが、このとき出力がバッファに溜まったままにならないよう、必要に応じてstdout.flushで明示的にフラッシュすることも検討してください。大量のデータを出力する場合、細切れにwrite/writelnを呼ぶより、一度のwritefでフォーマットして出力したほうが効率的なこともあります。逆にシンプルな文字列連結程度であればwritelnで十分でしょう。このように各関数の特性を理解し、可読性性能のバランスを考えながら使い分けることが、D言語での出力処理のベストプラクティスと言えます。

D言語の標準入力からのデータ読み取り方法:readln関数の基本的な使用法と応用テクニックを徹底解説

プログラムにユーザーからのデータ入力を受け付けるには、標準入力(キーボード入力)からの読み取りが必要です。std.stdioモジュールには、コンソールからの入力を簡単に行うための関数が用意されています。このセクションでは、代表的なreadln関数を中心に、標準入力からデータを読み込む方法や注意点について説明します。

readln関数の使い方:標準入力から1行の文字列を読み取る基本手順

write("入力してください: "); string line = readln(); writeln("入力された内容: ", line); 

readln関数は、標準入力から1行の文字列を読み取る基本的な関数です。エンターキーが押されるまでの入力を文字列として返し、変数に格納できます。例えば上のコードでは、まずプロンプトを表示した後、readln()でユーザーの入力を受け取り、それをlineという文字列変数に保存しています。ユーザーが「Dlang」と入力してエンターを押した場合、readln()は文字列"Dlang"を返し、その値がlineに入ります。その後writelnlineの内容を表示すると、入力された内容: Dlangのように出力されます。readlnによって読み込まれる文字列には末尾の改行(エンター)は含まれないため、改行コードを気にせず文字列処理を行えます。

ユーザー入力の処理方法:プロンプト表示とreadlnでデータを取得する方法を解説

標準入力から値を読み取る際には、単にreadlnを呼ぶだけでなく、事前にプロンプトを表示してユーザーに入力を促すのが一般的です。上記の例でも"入力してください:"というメッセージを出力しています。このように案内を表示する場合、writeを使って改行せずにメッセージを出し、その後にreadlnで入力待ちにするのが自然な流れです(writelnで改行してしまうと、ユーザーの入力が次の行に表示され、やや見づらくなります)。なお、writeでプロンプトを表示した後に即座にreadlnを呼ぶと、バッファリングの関係でプロンプトが表示されないことがあります。そのため、必要に応じてstdout.flushを呼び出して出力を強制的にフラッシュし、メッセージが画面に出てから入力待ちになるようにすると良いでしょう。適切なプロンプトとreadlnの組み合わせにより、ユーザーと対話的にデータをやり取りできます。

文字列から数値への変換:readlnで得た入力をstd.conv.toで数値型に変換

readlnで取得した入力は文字列型なので、そのままでは数値計算に利用できません。そこで登場するのがstd.convモジュールのto関数です。例えば、ユーザーに整数値を入力してもらい、それを数値型の変数に格納するには次のようにします。コード中ではtext(文字列)をtext.to!int()と変換することでint型の値valueを得ています。この例では、読み取った整数に1を足して表示しています。to!<型名>は文字列から指定した型への変換を行う汎用関数で、変換できない文字(数字以外)が含まれていると例外を投げます。変換の際にはimport std.conv;が必要な点に注意してください。readlnで読み込んだ文字列を数値に変換する処理は、ユーザー入力を扱う上で頻出するパターンです。

複数の値の読み取り:readfを使って一度に複数の入力を処理する方法

readlnは一度に一行分のテキストしか取得できませんが、readf関数を使うと一度に複数の値を読み取ることができます。readfはC言語のscanfのようにフォーマット指定子を用いて入力をパースする関数です。例えば、ユーザーに2つの整数をスペース区切りで入力してもらう場合、次のようにします。int a, b; readf("%d %d", a, b); と記述すると、一度のreadf呼び出しで2つの整数を読み込み、それぞれ変数aとbに格納します。ユーザーが10 20と入力すれば、a=10, b=20となります。改行や区切り文字に対応した書式を指定でき、例えば"%s\n%s\n"のようにすれば複数行にわたる入力も処理可能です(実際にはreadlnを組み合わせた方がわかりやすい場合もあります)。readfは読み取った項目数(マッチした数)を返すため、戻り値をチェックすることで入力成功/失敗の判定も可能です。複数の値を一度に取得したいときには、このreadfが便利です。

入力エラーとEOFの扱い:無効な入力や終端に対する対策方法

標準入力の読み取りでは、エラー処理や特殊な状況(EOF)の扱いにも注意が必要です。readlnがファイルの終端(EOF)に達した場合、戻り値としてnullを返します。そのため、auto line = readln(); if(line is null) { ... }のように判定すれば、入力の有無を検出できます。また、ユーザーが何も入力せずにEnterを押した場合は、空文字列(lengthが0の文字列)が返ります。この場合、数値変換しようとするとエラーになるため、if (!line.empty) { ... }でチェックしてからto!intを呼ぶようにします。to関数で変換できない文字列を渡すとConvExceptionという例外が投げられるため、try-catchで捕捉して再入力を促すか、あらかじめline.strip()で不要な空白を除去しておくなどの対策が必要です。一方、readfを使った場合は、関数の返り値(読み取れた項目数)を確認することで、必要なだけの入力が得られたかを判断できます。期待した数の値が読み取れなければ再試行する、といったロジックを組み込むと頑健になります。このように、標準入力からの読み取りではEOFや不正な入力に備えた対策を実装しておくことが重要です。

D言語File型によるファイル入出力の基礎:ファイルを開閉してデータを読み書きする方法を詳しく解説

標準入出力に加えて、ファイルへの読み書きもプログラミングにおいて重要な要素です。std.stdioモジュールには、File型というファイル操作用の型と関連する機能が用意されており、これを使ってテキストファイルの読み書きなどを行うことができます。本セクションでは、File型によるファイル入出力の基本について見ていきましょう。

File型とは何か:C言語のFILEを安全に扱うD言語のファイルオブジェクトの概要

File型は、D言語においてファイルを扱うための構造体(struct)です。内部的にはC言語のFILE(ファイルハンドル)をラップしており、低レベルなファイル操作を安全かつ便利に利用できるよう設計されています。File型の特徴は、ファイルのオープンやクローズの処理を自動化していることです。C言語ではfopenで開いたらfcloseで明示的に閉じる必要がありますが、D言語のFileではスコープを抜けたとき(変数が破棄されるとき)に自動でファイルを閉じてくれます(RAIIによるリソース管理)。さらに、File型はコピー可能で、同じファイルハンドルを参照するFile変数を複数持つこともできます(内部で参照カウントを管理)。このように、File型を使うことで、ファイル操作時のリソースリークやポインタ操作の煩雑さを気にせず、直感的なインターフェースでファイル入出力を行えます。

ファイルを開く方法:File構造体のコンストラクタでファイルを開きモードを指定する方法

ファイルを開くには、File構造体のコンストラクタにファイル名とモード文字列を渡します。モードはC言語のfopenと同様に"r"(読み込み)、"w"(書き込み、新規作成/上書き)、"a"(追記)などの文字列で指定できます。例えば、既存のテキストファイルを読み込むにはauto inFile = File("data.txt", "r");、新しく書き込み用ファイルを作るにはauto outFile = File("output.txt", "w");のように書きます。バイナリファイルを扱う場合は"rb""wb"のようにbを付けることで改行コードの変換を抑止できます。Fileのコンストラクタはファイルオープンに失敗した際にstd.stdio.StdioException(実際には内部でErrnoException)をスローします。そのため、try-catchで失敗を捕捉してエラー処理を行うことも可能です。成功すればFile型のオブジェクトが返り、以降そのオブジェクトを通じてファイル読み書きを行います。ファイルはFileオブジェクトのライフタイムに紐付いて管理され、開いたファイルを使い終われば特に何もせずともスコープアウト時に自動で閉じられます。

ファイルへのテキスト書き込み:File.writeとFile.writelnメソッドによる出力操作

ファイルへのテキスト書き込みは、File型オブジェクトのメンバ関数であるwritewritelnを使って行えます。使い方は標準出力の場合とほぼ同じで、違いは出力先がコンソールではなく対応するファイルになる点だけです。例えば、auto file = File("hello.txt", "w");とファイルを開いた後、file.writeln("Hello file!");のように呼び出せば、その文字列がファイルに書き込まれ改行も付与されます。file.writeを使えば改行なしで書き込めます。さらに、file.writeffile.writeflnを使って書式指定出力をファイルに行うことも可能です。複数のFileオブジェクトを扱っている場合、それぞれのwrite/writelnは対応するファイルに出力します。同じファイルに対し複数のFile変数が参照を共有している場合(Fileをコピーした場合など)でも、いずれから書き込んだ内容も同じ実ファイルに反映されます。File型のwrite系関数は内部でバッファリングされており、大きなデータを書き込む場合でも効率的に動作します。必要に応じてfile.flush()メソッドでバッファの内容を明示的にディスクに書き出すこともできますが、通常は自動的に適切に処理されます。

ファイルからのデータ読み込み:File.linesやFile.readfを用いたテキスト入力処理

ファイルからデータを読み取る方法もいくつか用意されています。最もシンプルなのは、ファイルから1行ずつ文字列を読み出す方法です。File型にはbyLineというプロパティがあり、これを使うとファイルを行単位で読み込む範囲 (Range)を取得できます。例えば、auto file = File("log.txt", "r"); foreach (string line; file.byLine()) { ... }とすれば、ファイルlog.txtの各行を順番にline変数に読み込みながらループ処理できます。また、std.stdioにはlines(File)という関数もあり、同様にファイルから行を取得できます。より構造化された入力が必要な場合には、Fileオブジェクトに対して直接readf/readlnを呼ぶことも可能です(例:file.readf("%d %f", x, y)でファイルから数値を読み込む)。これらを駆使することで、テキストファイルの内容をプログラム内に取り込んで処理することができます。なお、ファイル読み取り時にも例外(ファイル不存在や権限エラーなど)に注意し、必要に応じて例外処理を行ってください。

ファイルのクローズとRAII:File型の自動リソース管理と明示的クローズ不要の仕組み

File型では前述の通り、ファイルクローズは自動化されています。Fileオブジェクトがスコープから外れたり、別のファイルを開いて上書きされたりすると、その時点で参照カウントが減少し、最終的にどの変数からも参照されなくなったファイルは自動で閉じられます。そのため、C言語で必要だったfclose呼び出しを明示的に行う必要は基本的にありません。例えば、以下のようにローカルブロックを作ってFileを扱えば、そのブロックを抜けた時点でファイルがクローズされます。

{ File f = File("temp.txt", "w"); f.writeln("一時ファイルへの出力"); } // ここでfがスコープを抜け自動的にクローズ 

このように、リソース管理がRAIIによって自動化されているため、コーディングの手間やミスが減ります。ただし、長時間開きっぱなしにする必要がないファイルは、できるだけ早めにスコープから出してクローズさせるようにする(あるいは明示的にFile変数をnullに設定して参照を切る)ことが望ましいです。総じて、File型のおかげで開いたファイルを閉じ忘れるというミスは起きにくくなっていますが、リソース管理のタイミングを把握して適切にスコープを管理することが重要です。

D言語におけるバイナリファイルの入出力:File.rawReadとrawWriteを使った低レベルI/Oの効率的データ処理を詳しく解説

テキストデータ以外に、画像や実行ファイルなどのバイナリデータを扱う場面も多くあります。D言語のstd.stdioでは、バイナリファイルを効率よく読み書きするための低レベル関数としてrawReadrawWriteが提供されています。本セクションでは、テキスト入出力との違いやrawRead/rawWriteの使い方、バイナリI/O時の注意点について解説します。

バイナリデータを扱う必要性:テキスト入出力とバイナリ入出力の違いと用途

通常のreadlnwrite/writelnはテキストデータを前提としており、改行コードの変換(特にWindowsでは"\r\n""\n"の変換)などが自動で行われます。しかし、バイナリデータを扱う場合には、データを一切変換せずそのままのバイト列として読み書きする必要があります。例えば画像ファイルや音声ファイル、あるいは独自のバイナリ形式のデータでは、0x0A(改行)などの特殊なバイトもデータの一部であり、勝手に書き換えられては困るからです。また、テキスト入出力用の関数では、特定のバイト値(null文字0x00など)を終端とみなすような挙動がある場合もあり、バイナリ全体を正確に扱うのには適しません。そこで必要となるのがバイナリモードでの入出力です。D言語ではファイルを開く際にモードに"b"を付けるだけでなく、実際の読み書きにも専用の関数を使うことで、データをそのまま扱えます。バイナリ入出力は主に、ファイルコピーやシリアライズ、画像・映像処理など、生データを扱うユースケースで重要になります。

rawReadとrawWriteとは何か:バイナリ読み書き用低レベルI/O関数の概要

rawReadrawWriteは、std.stdioが提供する低レベルなバイナリ入出力用の関数です。名前の通り生のバイトデータを読み書きするためのもので、テキストデータに対する特別な処理を一切行いません。これらの関数はFile型のメンバ関数(または同等のfree関数)として利用でき、C言語でいうfread/fwriteに相当します。例えば、rawWriteはバッファ(配列)に格納されたバイト列をそのままファイルに書き出し、rawReadはファイルから指定バイト数を直接読み込んでバッファに格納します。Windows環境でも自動的にテキストモードからバイナリモードに切り替わるため、改行コードの変換などは行われません。これにより、開いたファイルがテキストかバイナリかを意識せず、常に生データとして扱うことが保証されます。rawRead/rawWriteは大量のデータを一括で読み書きするのに適しており、高速に動作するよう設計されています。次に具体的な使用方法を見てみましょう。

rawReadの基本的な使い方:File.rawReadで任意のバイトデータを読み込む手順

auto file = File("image.png", "rb"); ubyte[256] buf; size_t bytesRead = file.rawRead(buf); writeln("読み込んだバイト数: ", bytesRead); 

rawReadを使うには、まず読み込み先となるバッファ(配列)を用意します。例えば、256バイトずつファイルを読み込む場合は次のようにします。上記のコードでは、まずファイルimage.pngをバイナリ読み込みモード ("rb") で開き、ubyte[256]の固定長配列bufを用意しています。file.rawRead(buf);を呼ぶと、ファイルから最大でbufのサイズ(ここでは256バイト)分のデータを読み込み、bufに格納します。戻り値のbytesReadには実際に読み込まれたバイト数が格納されます(EOFに達して256未満のデータしか読めなかった場合などは、その分だけ読み込み量が少なくなります)。rawReadはバッファが小さければ一部のみ、大きければ複数回に分けて呼び出すことでファイル全体を順次読み込むことができます。なお、rawReadはバイナリ専用のため、文字エンコーディングの解釈などは一切行わず、ファイルの内容をそのまま取り出す点に留意してください。

rawWriteの基本的な使い方:File.rawWriteで任意のバイトデータを書き出す手順

auto out = File("copy.bin", "wb"); ubyte[] data = [0x01, 0x02, 0x03, 0x04]; out.rawWrite(data); 

一方、ファイルへのバイナリ書き込みにはrawWriteを使用します。使い方はrawReadと対称的で、書き込みたいデータをバイト配列などに用意し、それをrawWriteに渡すだけです。例えば、4バイトの配列[0x01, 0x02, 0x03, 0x04]をファイルに書き出するには上記のようにします。このコードでは、新規ファイルcopy.binをバイナリ書き込みモード ("wb") で開き、data配列の内容をout.rawWrite(data);で一括出力しています。rawWriteは引数として配列(任意の型の連続データ)を取るテンプレート関数で、配列全体をそのバイト表現のままファイルに書き込みます。戻り値として書き込んだ要素の個数(ここでは4)が返されます。rawWriteもrawRead同様、高速なI/Oのためにバッファリングが行われます。大量のデータを扱う際には、適切なサイズのバッファでrawRead/rawWriteを繰り返すことで効率的にファイルコピーやバイナリ処理を行うことができます。

バイナリI/Oの注意点:エンディアンやデータサイズなど低レベル処理での考慮事項

バイナリ入出力を行う際には、いくつか注意すべき点があります。まず、データのエンディアン(バイト序列)です。rawRead/rawWriteはメモリ上のバイト列をそのまま扱うため、例えばマシンがリトルエンディアン(Intel系)の場合は整数をそのバイト順(下位バイトから)でファイルに書き出します。他のプラットフォームで読み込む可能性があるデータを扱う際には、エンディアンを統一する(必要に応じてバイト順を入れ替える)対策が必要です。また、rawWriteで構造体や数値配列を直接書き込む場合、コンパイラが補間したパディングバイトまで含めて出力される可能性があります。互換性を保つには、予めデータ形式を定め、必要なら手動でシリアライズ処理を行う方が安全です。さらに、rawRead/rawWriteを使う際は、一度に読み書きするバイト数に注意し、想定外のEOFやバッファオーバーランを防ぐようにします(戻り値で読み書き量を必ず確認しましょう)。以上の点に留意すれば、rawRead/rawWriteは強力なバイナリI/O手段として活用できます。

D言語の標準入出力ストリーム(stdin / stdout / stderr)の役割と正しい使い方を徹底解説

コンソール入出力に関連して、標準ストリームと呼ばれる3つのデータ流があります。すなわち、標準入力 (stdin)、標準出力 (stdout)、標準エラー (stderr) です。std.stdioモジュールでは、これらの標準ストリームを操作するためのオブジェクトが用意されており、様々な用途に活用できます。本セクションでは、stdin・stdout・stderrの役割と利用方法について解説します。

標準ストリームとは何か:stdin・stdout・stderrの基本的な役割と違いを解説

標準ストリームとは、プログラムがデフォルトで使用する入出力の流れのことです。標準入力 (stdin) はプログラムへの入力のデフォルトの流れで、通常はキーボードからの入力がここに入ります。標準出力 (stdout) はプログラムからの通常の出力の流れで、特に指定しなければコンソール画面にテキストが表示されます。標準エラー (stderr) はエラーメッセージやログなどを出力するための別系統の出力で、通常はstdoutと同じコンソールに表示されますが、別途リダイレクトすることでログファイルに記録する等の運用が可能です。stdoutとstderrを分けておくことで、プログラムの正常な出力とエラー出力を別々に扱うことができます。例えば、パイプで他のコマンドに標準出力を渡しつつ、エラーだけは画面に表示する、といった柔軟な制御が可能になります。

stdin・stdout・stderrの実体:D言語ではFile構造体として実装される標準ストリーム

D言語では、stdin・stdout・stderrはstd.stdioモジュール内でFile構造体のオブジェクトとして提供されています。つまり、これらは通常のFile型と同様にwritereadfなどのメソッドを呼び出すことができます。例えば、stdout.writeln("メッセージ")とすれば標準出力に文字列を表示し、stderr.writef("Error code: %d", code)のようにすれば標準エラーにフォーマット済みメッセージを出力できます。また、stdin.readln()と呼べばキーボード入力から1行読み取ることができます。内部的には、これらのオブジェクトはC言語のstdin/stdout/stderrに対応したファイルハンドルをラップしており、プログラム開始時に自動で初期化されています。特別な準備をせずともすぐに使えるため、標準出力への出力には前述のように単にwriteln関数(内部でstdoutを利用)を使えばよいわけですが、必要に応じて明示的にstdoutオブジェクトを操作することもできます。

標準出力(stdout)の使用方法:writelnやwriteで画面に出力する基本手順

標準出力 (stdout) は、デフォルトではwritewriteln関数が使用している出力先です。つまり、writeln("Hello")と呼ぶのは内部的にはstdout.writeln("Hello")と同等です。多くの場合、明示的にstdoutオブジェクトを触る必要はありませんが、例えば標準出力を関数に渡して別の処理系で使う場合や、出力をフラッシュする必要がある場合に直接操作することがあります。stdout.write/stdout.writelnの使い方自体は先述したFile型の書き込みと同じです。また、stdout.flush()を呼べば、バッファに溜まっている出力を即座に画面に書き出します(通常、改行が出力されると自動的にフラッシュされますが、改行なしのwriteを使ったときなどは明示的なflushが役立ちます)。まとめると、標準出力への出力はシンプルにwritelnなどを使えばよい一方で、必要に応じてstdoutオブジェクトを直接操作する方法も用意されています。

標準エラー(stderr)の使用方法:エラーをstderrに出力して標準出力と分離する利点

標準エラー (stderr) は、エラーメッセージや警告などを出力するためのストリームです。通常の出力と分離することで、エラーだけを別途ログに記録したり、画面上で目立たせたりできます。D言語では、stderr.writeln(...)stderr.writef(...)を使って簡単にメッセージを標準エラーに出力できます。例えば、ファイル読み込みに失敗した際にstderr.writeln("ファイルが見つかりません")とすれば、そのメッセージは標準エラーとして出力されます。ユーザーがプログラムの標準出力をファイルにリダイレクトしていても、標準エラー出力は通常のコンソールに表示されるため、エラーメッセージを見逃しにくくなります。また、シェル環境では2>error.logのように指定することで、stderrの内容だけをファイルにリダイレクトすることも可能です。stderrを使い分けるメリットとして、正常系の出力と異常系の出力を明確に区別し、後段の処理(パイプ等)やログ管理を柔軟に行える点が挙げられます。D言語でもこの習慣にならい、エラー出力にはstderrを用いるのが一般的です。

標準入力(stdin)からの読み取り:キーボード入力やパイプからデータを受け取る方法

標準入力 (stdin) からのデータ読み取りは、これまで説明してきたreadlnreadfを使えば十分です。実際、それらの関数は内部的にstdinを利用しています。たとえば、readln()と書くのはstdin.readln()を呼んでいるのと同じです。したがって、stdinオブジェクトを明示的に扱わなくとも、前述の方法でキーボード入力を取得できます。しかし、stdinを直接使うことで柔軟な読み取りも可能です。例えば、foreach (line; stdin.byLine()) { ... }とすれば、標準入力から来る複数行のデータを逐次処理できます。このコードは、ユーザーがキーボードから複数行入力する場合だけでなく、プログラム実行時に< input.txtのようにファイルをパイプで与えられた場合にも、その内容を順次lineに読み込みます。つまり、stdin経由であればキーボード入力であろうとファイルからのリダイレクトであろうと同一のコードで処理できるのです。D言語では標準入力もFile型として抽象化されているおかげで、入出力元を意識せず一貫した方法でデータを読み取れるという利点があります。

D言語における例外処理とエラー出力:writelnとstderrを使い分けたエラーメッセージ処理のベストプラクティス

プログラムがファイルのオープンに失敗したり、ユーザーの入力が不正だった場合など、エラー状況への対処が必要です。D言語では例外機構を用いてエラー処理を行うのが一般的であり、std.stdioでも入出力エラー時に例外が投げられます。また、エラーメッセージの出力には先述の標準エラー (stderr) を使うことで、正常な出力と分離して扱うことが推奨されます。本セクションでは、D言語における例外処理の基本と、エラー出力の適切な方法について説明します。

標準出力と標準エラーの使い分け:エラーメッセージをどのストリームに出力すべきか

まず前提として、エラーメッセージは標準出力ではなく標準エラーに出力するのが望ましいという点があります。通常の計算結果や処理結果は標準出力 (stdout) に出力し、一方で異常やエラーに関するメッセージは標準エラー (stderr) に出力することで、両者を明確に分離できます。例えば、プログラムの出力を別のコマンドにパイプで渡して利用する場合、標準出力にエラーメッセージが混じってしまうと後段の処理が誤動作する可能性があります。しかし、エラーをstderrに出しておけば、パイプで渡されるのは正常データのみとなり、エラーメッセージはコンソールに表示されるだけでパイプには流れません。このように、エラーメッセージの出力先を使い分けることは堅牢なプログラムを書く上で基本的な習慣です。D言語においても、stderr.writeln(...)などを活用してエラー内容を標準エラーに出力するようにしましょう。

標準エラー(stderr)を使うケース:エラー出力を分離するメリットと具体例

では、具体的にどのようなケースで標準エラーを使うべきか、そのメリットを考えてみます。典型的な例として、ファイルを開こうとしたが存在しない場合があります。このとき、"ファイルが見つかりません"というメッセージを標準出力に出してしまうと、プログラムの通常の出力に混ざってしまい、出力結果をパースしている他のプログラムやスクリプトが誤って解釈する恐れがあります。代わりにstderr.writeln("ファイルが見つかりません")とすれば、そのメッセージは標準エラーとして出力され、通常の出力とは別枠で表示・記録できます。また、コマンドラインツールでヘルプメッセージや警告を表示する際にも、stderrを使うことで本来の処理結果(標準出力)と混同しないようにできます。要するに、「プログラムの主要な出力ではない情報」(エラー、警告、進捗ログなど)は標準エラーに出力するのが適切であり、そのメリットは結果の明確な分離と、出力先の柔軟な制御(必要に応じてエラーだけログにリダイレクト可能)にあります。

D言語における例外処理の基本:throwとcatchを使ったエラー対策の方法

import std.stdio; void main() { try { File f = File("no_such_file.txt", "r"); // ファイル読み込み処理... } catch (Exception e) { stderr.writeln("ファイルオープン失敗: ", e.msg); } } 

D言語におけるエラー処理は、例外を使ったtry-catch構文で行います。上記のコード例のように、エラーが発生しうる処理をtryブロックに入れ、catchブロックで例外オブジェクトを受け取って処理します。例では存在しないファイルを開こうとして例外が投げられ、それをcatch(Exception e)で捕捉しています。ExceptionはD言語のすべての例外の基底クラスで、e.msgプロパティにはエラーメッセージが格納されています。このメッセージを標準エラーに出力することで、エラーの内容をユーザーに伝えています。D言語の標準ライブラリ関数は、重大なエラー時に戻り値でなく例外を投げる設計になっているものが多く、std.stdioのFileオープン失敗や読み書きエラーも例外(StdioExceptionErrnoException)によって通知されます。適切に例外を捕捉し、エラーメッセージをstderr経由で表示することで、堅牢でわかりやすいエラー処理を実現できます。

ファイルI/Oで発生する例外:File操作で投げられるStdioExceptionやErrnoExceptionの扱い

std.stdioモジュールで発生し得る代表的な例外として、ファイル操作に関するStdioExceptionErrnoExceptionがあります。たとえば、Fileのコンストラクタでファイルを開けない場合や、読み書き中にOSレベルのエラーが発生した場合、これらの例外がスローされます。ErrnoExceptionはPOSIX系のエラーコード(errno)を含む例外で、失敗したシステムコールのエラー内容(ファイル未発見、アクセス拒否など)がe.msge.errno経由で取得できます。StdioExceptionはstd.stdio内で発生する一般的な入出力例外で、内部的にErrnoExceptionを継承している場合があります。通常は基底クラスのExceptionIOException(入出力関連の例外の基底)でまとめて捕捉してしまって問題ありませんが、必要に応じて特定の例外型ごとにcatchブロックを分けることもできます。重要なのは、これらの例外が発生し得ることを念頭に置き、try-catchで適切にハンドリングすることです。特にファイル入出力では環境依存のエラー(ファイルなし、権限なし、ディスク容量不足等)が起こり得るため、例外処理によって異常系に対処する実装が欠かせません。

例外発生時のエラーメッセージ表示:catchブロック内でstderr.writelnを活用する方法

例外を捕捉した際に、その内容をユーザーに伝える手段として、先に述べた標準エラーへの出力が役立ちます。catchブロック内でstderr.writeln("エラー: ", e.msg)のように書けば、例外オブジェクトが持つエラーメッセージをユーザーに通知できます。必要であればe.errnoe.toString(スタックトレースを含む詳細情報)を出力することも可能です。D言語では、捕捉されなかった例外が発生するとプログラムは異常終了し、そのスタックトレースが自動的に標準エラーに表示されます。しかし、ユーザーにとっては生のスタックトレースよりも、意味の分かりやすいメッセージの方が親切です。したがって、想定されるエラーについてはcatchで捕捉し、適切な日本語や具体的な内容を含むメッセージをstderr経由で表示するようにしましょう。また、エラー終了時には適切な終了コードを返す(D言語ではThrowableを捕まえずにmainを抜けると自動で非0の終了コードになります)ことも忘れないようにします。総じて、例外処理と標準エラー出力の組み合わせにより、エラー発生時でもユーザーに情報を提供しつつプログラムの健全性を保つことができます。

std.stdioとstd.file・std.conv・std.algorithmの連携例:標準入出力を活用した他モジュールとの実践的な組み合わせ

std.stdio単独でも基本的な入出力は可能ですが、D言語の標準ライブラリには他にも便利なモジュールが多数あります。特に、ファイル全体の操作を行うstd.file、データ型変換を行うstd.conv、およびデータ処理用のアルゴリズムを提供するstd.algorithmは、std.stdioと組み合わせて用いることで入出力処理を一層強力かつ簡潔にできます。本セクションでは、これらのモジュールとの連携方法と実用例について紹介します。

std.fileとの連携:std.fileの便利関数を使ってファイル全体を読み書きする方法

std.fileモジュールは、ファイル操作全般を簡素化する便利関数を提供しています。例えば、ファイル全体を一度に読み込む場合、std.stdioであればFileを開いてループで読み込む必要がありますが、std.fileのreadText関数を使えばstring content = readText("input.txt");だけでファイルの中身を文字列として取得できます(テキストファイルの場合)。バイナリファイルであればauto data = read("image.png");のようにread関数でubyte[](バイト配列)として丸ごと読み込めます。書き込みも同様で、write("output.txt", content);とすれば文字列contentの内容をファイルに一括で書き出せます。これらの関数は内部でstd.stdioのFileを使用していますが、開閉の処理を意識せずに使えるため非常に手軽です。その他にも、ファイルやディレクトリの存在確認exists、削除remove、コピーcopy、作成mkdirといったユーティリティ関数も提供されており、std.stdioと組み合わせて使うことで、低レベルから高レベルまで網羅したファイル操作が可能になります。必要に応じてstd.fileを活用することで、std.stdioでは手動で実装していた処理を一行で済ませられる場合も多いでしょう。

std.convとの連携:入出力文字列をto関数で数値や別の型に変換するテクニック

std.convモジュールは、データ型の変換(コンバージョン)を提供するモジュールです。std.stdioで標準入力やファイルから読み込んだデータは文字列であることが多いですが、プログラム内で計算するには数値や他の型に変換する必要があります。そこで役立つのがstd.convのto関数です。前述したように、to!int("123")とすれば文字列"123"を整数123に変換できますし、to!double("3.14")なら3.14という浮動小数点数に、to!bool("true")なら真偽値trueに変換できます。逆に数値から文字列への変換もto!string(値)で可能です(もっとも、標準出力に出すだけならwritelnが自動で変換しますが、文字列操作が必要な場合に役立ちます)。また、複合的な型への変換(例えば日付文字列をDate型に変換など)にも対応しており、std.convは汎用的な変換ルーティンとして非常に有用です。標準入出力と組み合わせることで、入力→文字列→目的の型という処理を簡潔に書けるようになるため、std.stdioを使う際にはstd.convも併せてインポートしておくと便利でしょう。

std.algorithmとの連携:読み取ったデータにfilterやsortを適用して処理する方法

データ処理に関しては、std.algorithmモジュールが強力な機能を提供します。例えば、ファイルから読み込んだ多数の数値をソートしたり、特定の条件でフィルタリングしたりする場合、従来ならループを書いて処理していたところを、std.algorithmの関数でもっと簡潔に記述できます。std.algorithmにはfilter(条件に合う要素の抽出)、map(要素の変換)、reduce(畳み込み演算)、sort(ソート)など多彩なアルゴリズムが用意されており、D言語のRange(範囲)と組み合わせてパイプライン的なデータ処理が可能です。例えば、次のようなコードを考えます。

import std.stdio; import std.conv; import std.algorithm; void main() { // 標準入力から整数を読み取り、非負のものだけソートして表示 auto nums = stdin.byLine() .map!(line => line.to!int) .filter!(n => n >= 0) .array; sort(nums); writeln("非負の数を昇順ソート: ", nums); } 

上記のコードでは、標準入力から読み込んだ複数行の数値について、負の値を除外し、残った非負の数を昇順にソートしています。stdin.byLine()で得た行の範囲に対し、map!(line => line.to!int)で文字列を整数に変換し、次にfilter!(n => n >= 0)で0以上の値だけを通しています。最後にarrayで結果を配列にし、sort(nums);でソートしています。このように、std.stdioで取得したデータ(Range)に対してstd.convのtoとstd.algorithmのmap/filter/sortを組み合わせることで、データ処理の流れを宣言的かつ簡潔に記述できます。D言語ならではの強力な標準ライブラリ機能を活用することで、入出力から加工・出力までの一連の処理を効率よく実装できるのが分かるでしょう。

CSV処理の実践例:std.stdioで読み込んだデータをstd.convで解析しstd.algorithmで加工

実践例1: 例えば、CSV形式のデータファイルを読み込んで処理するケースを考えてみます。std.stdioでファイルを1行ずつ読み込み(File.byLine()を使用)、各行をstd.algorithmのsplitterやstd.stringのsplitでカンマ区切りに分割します。次に、std.convのtoを使って文字列から数値や他の型に変換し、std.algorithmのfilterで必要なデータだけ抽出したり、mapで計算を適用したりできます。例えば、CSVに含まれる数値データの合計や平均を求めたい場合、各行についてmap!(fields => fields[列番号].to!int)で特定列の数値を取り出し、reduceで総和を計算、件数で割ることで平均を算出、といった処理をパイプライン的に書くことができます。std.stdioでの入出力と、std.conv/std.algorithmでのデータ変換・操作を組み合わせることで、CSV解析のような一連の処理もシンプルなコードで実現可能です。

テキスト処理ツールの例:std.stdioと他の標準ライブラリを組み合わせてシンプルなユーティリティを作成

実践例2: 標準入力と標準出力を使った簡単なテキスト処理ツールを考えてみましょう。例えば、入力テキストから空行やコメント行("#"で始まる行)を除去するフィルタプログラムは、std.stdioとstd.algorithmだけで容易に実装できます。stdin.byLine()で全入力行の範囲を取得し、filter!(line => !line.empty && !line.startsWith("#"))で必要な行だけ通過させ、その結果をstdout.writeln(line)で出力するだけです。これにstd.convの処理が必要なら適宜mapで型変換を差し込み、std.fileの機能が必要ならファイル全体を読み込んでから処理することもできます。このように、標準入出力と他の標準ライブラリモジュールを組み合わせることで、ちょっとしたユーティリティから高度なデータ処理プログラムまで効率よく作成できます。D言語の豊富な標準ライブラリ群は、それらを連携させることでより大きな効果を発揮するでしょう。

実用的なサンプルコード集:小さなユーティリティやテンプレートで学ぶstd.stdioの活用例を厳選して丁寧に解説

最後に、std.stdioを使った実用的なサンプルコードをいくつか紹介します。小規模なユーティリティやテンプレート的なプログラムを通して、std.stdioの活用方法を具体的にイメージしてみましょう。以下の例では、本記事で解説してきた標準入出力のテクニックや他のモジュールとの連携を盛り込みつつ、D言語らしい簡潔なコードで実現しています。

ユーティリティ例1:テキストファイル内の行数をカウントするプログラムの実装と解説

import std.stdio; import std.string; void main() { write("行数を数えたいファイル名: "); string filename = readln(); if (filename.isNull) { return; // EOF } filename = filename.strip(); size_t count = 0; try { File file = File(filename, "r"); foreach (_; file.byLine()) count++; } catch (Exception e) { stderr.writeln("ファイルを開けませんでした: ", e.msg); return; } writeln("行数: ", count); } 

このプログラムは、指定されたテキストファイルの行数を数えて表示するユーティリティです。まずユーザーにファイル名を入力してもらい(プロンプトを標準出力に表示)、readlnでそのファイル名を取得しています。stripを呼んで改行を除去した後、File(filename, "r")でファイルを開き、foreachfile.byLine()を回して1行読むごとにカウンタをインクリメントしています。例外処理も組み込んでおり、ファイルが開けなかった場合にはcatch節でエラーメッセージを標準エラーに出力して終了します。正常に全行読み終えれば、最後に行数を標準出力に表示します。File.byLine()を使うことでメモリに全行を読み込まずに効率よく行数カウントができ、非常にシンプルなコードで実装できていることが分かります。

ユーティリティ例2:数値を読み込み統計量(合計・平均)を計算するプログラムの実装

import std.stdio; import std.string; import std.conv; void main() { write("数値をスペース区切りで入力してください: "); string line = readln(); if (line.isNull || line.empty) { return; } line = line.strip(); string[] parts = line.split(); long sum = 0; foreach(part; parts) { sum += part.to!long(); } double average = parts.length ? cast(double) sum / parts.length : 0; writeln("合計: ", sum); writeln("平均: ", average); } 

このプログラムは、複数の数値を入力してもらい、その合計平均を計算して表示するものです。ユーザーにはスペース区切りで数値を入力してもらい、readlnで一行分の文字列として取得しています。split()関数で空白区切りのトークンに分割し、それぞれをto!long()で整数に変換して合計を計算しています。平均値は、合計を個数で割ることで求めています(整数のまま割ってしまわないよう、doubleにキャストしています)。最後に、合計と平均を標準出力に表示します。std.stdioのreadlnで入力を取得し、std.stringのsplitでトークン化、std.convのtoで変換という流れで、複数の数値入力に対する集計処理を簡潔に実現しています。

ユーティリティ例3:標準入力のテキストを加工して出力する簡易フィルターの実装例

import std.stdio; void main() { size_t lineNo = 1; foreach (string line; stdin.byLine()) { writeln(lineNo, ": ", line.idup); lineNo++; } } 

このプログラムは、標準入力からテキストを読み込み、各行の先頭に行番号を付与して出力するものです。stdin.byLine()で入力を1行ずつ読み込み、foreachループ内で現在の行番号 (lineNo) と": "を付けた後、行の内容をそのまま標準出力に書き出しています。例えば、入力が

Apple Banana Cherry 

だった場合、出力は

1: Apple 2: Banana 3: Cherry 

のようになります。空行も含めて行番号が振られます。stdin.byLine()で得られる行はデフォルトでは一時的な参照のため、ここではline.idupで内容をコピーしています(単に表示するだけなら必須ではありませんが、安全のためコピーしています)。このユーティリティは、ファイル内容の行番号付けや簡易なテキストビューアとして利用できます。std.stdioの標準入力読み取りと標準出力書き出しを組み合わせた、シンプルながら実用的な例と言えるでしょう。

ユーティリティ例4:ログファイルを解析しエラーメッセージのみstderrに出力するツールの実例

import std.stdio; import std.string; void main() { string logFile = "application.log"; File file; try { file = File(logFile, "r"); } catch (Exception e) { stderr.writeln("ログファイルを開けません: ", e.msg); return; } foreach (string line; file.byLine()) { if (line.startsWith("ERROR")) { stderr.writeln(line.idup); } } } 

このプログラムは、アプリケーションのログファイルからエラー行だけを抜き出して標準エラーに出力するフィルタです。コードでは、まずapplication.logファイルを開き(エラー時は例外を捕捉してメッセージ表示)、foreachで各行を読み込んでいます。各行について、line.startsWith("ERROR")でその行が"ERROR"で始まるかを確認し、該当する場合に限りstderr.writeln(line)でその行内容をエラー出力しています。つまり、ログ中のエラーメッセージ行だけが標準エラーに書き出され、他の行は無視されます。このようなツールを使えば、長大なログからエラー情報だけを抽出して別ファイルにリダイレクトする、といったことが簡単に行えます。std.stdioでファイルを扱いながら、条件判定によってstderrに出力することで、正常系と異常系のログを分離して処理できる実例です。

ユーティリティ例5:std.stdioを使った基本的なテンプレートプログラムの構築方法

import std.stdio; void main(string[] args) { if (args.length != 3) { stderr.writeln("Usage: mytool  "); return; } string inputName = args[1]; string outputName = args[2]; try { File inFile = File(inputName, "rb"); File outFile = File(outputName, "wb"); foreach (ubyte[] buf; inFile.byChunk(4096)) { outFile.rawWrite(buf); } } catch (Exception e) { stderr.writeln("ファイル処理エラー: ", e.msg); return; } writeln("ファイルコピーが完了しました"); } 

このコードは、コマンドライン引数で指定した入力ファイルを出力ファイルにコピーするシンプルなプログラムです。プログラム名に続けて<入力ファイル> <出力ファイル>を指定して実行すると、その内容をバイナリモードでコピーします。まず引数の数をチェックし、不正な場合は使用方法を標準エラーに表示して終了します。正しい引数が与えられれば、File(inputName, "rb")で入力ファイルを、File(outputName, "wb")で出力ファイルを開きます。inFile.byChunk(4096)はファイルを4096バイトずつ読み込むRangeを返すので、それをforeachで回しながらoutFile.rawWrite(buf)で逐次書き出しています。4096バイトというチャンクサイズは任意ですが、このようにすることで大きなファイルでもメモリを節約しつつ高速にコピーできます。途中で例外(ファイルが開けない等)が発生した場合はcatchで捕捉してエラーメッセージを表示し、正常終了した場合は「ファイルコピーが完了しました」と標準出力に表示します。このプログラムは、入出力の基本処理・引数の扱い・例外処理・標準エラーへの出力といった要素を全て含んでおり、std.stdioを活用したD言語プログラムの良いテンプレートになるでしょう。

資料請求

RELATED POSTS 関連記事