ソースコード内で、実行時は常に固定的な当て意を提供する情報をリテラル(定数)と呼びます。 リテラル情報はコンパイル時に判定され、実行ファイルに埋め込まれます。 例えば 10 という数字や 5.1 という少数、それから文字や文字列などが定数となります。 先のプログラムの "Kitty on your lap" という文字列は、文字列の定数、文字列リテラルなのです。
D 言語の文字列リテラルは、基本的に C 言語と同じですがウィズィウィグ文字列など新しい記法が追加されています。 通常の文字列はエスケープシーケンスなどが展開され、タブ文字や改行文字など、文字としては表現が難しい記号に変換することができました。 しかし、エスケープシーケンスを採用する場合は \ 文字を多用する文字列リテラルの表現が難しかったり、単純なミスを誘発する原因にもなります。
ウィズィウィグ文字列は、r" から始まり " で終わる文字列リテラルです。 通常の文字列とは違い、この文字列の中ではエスケープシーケンスは使うことができません。 What you see is what you get、WYSIWYG 文字列、つまり、画面に表示されるままに出力される文字列という意味です。
import std.stream; int main() { stdout.writeLine("-----通常の文字列-----"); stdout.writeLine("C:\\WINDOWS\\SYSTEM"); stdout.writeLine("Blue Blue Glass Moon,\n\tUnder the Crimson Air.\n"); stdout.writeLine("-----ウィズィウィグ文字列-----"); stdout.writeLine(r"C:\\WINDOWS\\SYSTEM"); stdout.writeLine(r"Blue Blue Glass Moon,\n\tUnder the Crimson Air.\n"); return 0; }
このプログラムを実行すれば、ウィズィウィグ文字列の効果を確認することができるでしょう。 通常の文字列は \ 記号はエスケープシーケンスとして判断されますが、ウィズィウィグ文字列はエスケープシーケンスではなく、そのままの文字として扱われます。
因みに、D 言語で利用できるエスケープシーケンスは、C 言語のエスケープシーケンス互換で次のようなものがあります。
エスケープ文字 | 意味 |
---|---|
\' | 引用符 |
\" | 二重引用符 |
\? | 疑問符 |
\\ | \ 記号 |
\a | アラート |
\b | 1 文字戻る |
\f | ページ送り |
\n | 改行 |
\r | リターン |
\t | 水平タブ |
\v | 垂直タブ |
\xXX | 16進数表記 (XX は 2 桁の16進数) |
\XXX | 8真数表記 (XXX は 3 桁までの8進数) |
\uXXXX | Unicode (XXXX は 4 桁の16進数) |
\UXXXXXXXX | Unicode (XXXXXXXX は 8 桁の16進数) |
推奨されませんが、ウィズィウィグ文字列には r" 以外に互い違いのウィズィウィグ文字列と呼ばれる記述方法があります。 互い違いのウィズィウィグ文字列は、二重引用符ではなくバッククォート文字 ` を使って文字列を囲みます。 この文字列も \ 記号がそのまま使われますが、さらに二重引用符を文字列の中で利用できるのです。 ウィズィウィグ文字列は \" を二重引用符として処理できないため、ウィズィウィグ文字列内で二重引用符を利用する場合は互い違いのウィズィウィグ文字列を使います。
import std.stream; int main() { stdout.writeLine(`"C:\WINDOWS\SYSTEM"`); return 0; }
このプログラムを実行すると、二重引用符もそのまま文字列の一部として解釈されるため "C:\WINDOWS\SYSTEM" と表示されるでしょう。
さらに、D 言語では 16 進数文字列と呼ばれる文字列の表現方法がサポートされています。 これは、その名前のとおり、16 進数による連続した文字コードとして解釈される文字列です。 16 進数文字列は x" で始まり " で終わります。
import std.stream; int main() { stdout.writeLine(x"4B69747479 20 6F6E 20 796F7572 20 6C6170"); return 0; }
このプログラムを実行すると、連続した 16 進数の文字コードが文字に変換され "Kitty on your lap" と表示されるでしょう。 16 進数文字列内には、16進数と空白などを除いたアルファベットなどの文字を指定することはできません。
また、C 言語の文字列の場合は二重引用符は一行の中で表現しなければなりませんでした。 ソースコードの可読性の問題から文字列を途中で改行させたい場合は、行連結文字 \ を末尾に指定するなどの対処が必要でした。 しかし、D 言語ではこのような必要はなくなり、文字列を途中で改行しても、最終的に二重引用符が対応していれば問題はありません。
import std.stream; int main() { stdout.writeLine("Blue Blue Glass Moon, Under the Crimson Air."); stdout.writeLine("Fate/\n" "\tStay night"); return 0; }
このプログラムは、文字列を途中で改行させたり、文字列リテラルを連続して出現させたりしていますが、問題はありません。 連続する文字列リテラルは、C 言語同様にコンパイル時に自動的に連結します。
C 言語を学習した人にとってはすでに認識できていることだと思われますが、D 言語も文字列は文字の列として認識されています。 そのため、単純な文字と、文字列は型として異なり、文字列は文字の配列型であると解釈されています。 D 言語の文字リテラルも、C 言語と同様に引用符 ' で 1 文字を囲みます。 エスケープシーケンスの場合は '\n' でも 1 文字として解釈されます。
import std.stream; int main() { stdout.write('K'); stdout.write('i'); stdout.write('t'); stdout.write('t'); stdout.write('y'); stdout.write('\n'); return 0; }
このプログラムは、個々の文字リテラルを write() メソッドで次々に出力します。 その結果、'K' 'i' 't' 't' 'y' が連続して表示されるため Kitty と画面に表示されるでしょう。
整数リテラルは、10 や 428945 など、整数による定数のことで、基本的な概念は C 言語と同じです。 整数リテラル間の計算はコンパイル時に行われて最適化され、常に固定的な値を提供し続けます。
問題は、どうやって数値型の定数を表示するかです。 C 言語の printf() 関数はフォーマットに対応していたため、様々な型を文字列に変換して表示することができました。 D 言語の標準である stream クラスの write() メソッドは、確かに様々な型を出力できますが、フォーマットは行わないのです。 例えば 100 を文字として出力すると、100 に対応した文字コード d が表示されてしまうのです。 数値をそのまま表示するには、一度文字列に変換しなければなりません。
整数リテラルを文字列に変換するには std . string モジュール内に宣言されている toString() 関数を利用します。 この関数は、与えられた数値を文字列に変換して返してくれます。
char[] toString(uint u)
D 言語の関数宣言や型についてはすぐ後で解説します。 この場では、数値を表示するために toString() 関数を呼び出せばよいということだけを覚えてください。
import std.stream; import std.string; int main() { stdout.writeString(toString(100)); return 0; }
このプログラムは、toString() 関数に指定した 100 という整数リテラルを文字列に変換し、変換した文字列を writeString() 関数で表示するというものです。 stream クラスは改行を行わずに文字列を表示する write() 関数がありますが、write() 関数はフォーマットを行わずに出力を試みるため、実装依存の傾向が強すぎます。 文字列を適切に、改行文字を付加せずに出力するには writeString() 関数を使ったほうが良いでしょう。
D 言語の整数リテラルには、任意の位置にアンダーライン _ を追加することができます。 整数リテラルのトークンに _ が存在する場合、コンパイラはこのアンダーラインを完全に無視してしまいます。 例えば 4_9980 は 49980 と認識され、1_2_3_4_5 は 12345 と認識されます。 このアンダーラインは、大きな数値を扱うとき、単位を間違えたりしないように区切り文字として利用するためのものです。
import std.stream; int main() { stdout.printf("1_2_3_4_5 = %d" , 1_2_3_4_5); return 0; }
このプログラムは、アンダーラインで区切られている整数リテラルを表示します。 目的の整数リテラル 1_2_3_4_5 は、期待通り 12345 として認識されることを確認することができます。 ただし、C 言語プログラマなど、他言語の出身者は互換性を考慮して、あまり積極的には利用しないでしょう。
通常、stream クラスで文字列を出力する場合は writeLine() か writeString() 関数が利用されますが、多くの C 言語プログラマが馴染んでいる printf() 関数も宣言されています。 そのため、stream クラスのオブジェクト stdout から printf() 関数を利用することができます。 やはり、C 言語の経験者にとってはこのような記述のほうが見やすいでしょう。
D 言語で 16 進数や 8 進数で整数リテラルを記述する方法も C 言語とまったく同じです。 8 進数表記は 0 からリテラルが始まり、0 〜 7 までの数を使って記述しなければなりません。 16 進数の場合は 0x または 0X で始まり、0 〜 9 までの数字と A 〜 F までの文字で表現します。 A 〜 F までの文字は小文字でも、大文字と小文字を組み合わせてもかまいません。
そして、さらに D 言語では 2 進数による整数リテラルの表記方法が加えられました。 2 進数で整数リテラルを表現するには 0b または 0B から始め、0 または 1 だけを使って目的の値を表現します。
import std.stream; int main() { stdout.printf("0b11111111 = %d\n" , 0b11111111); stdout.printf("0377 = %d\n" , 0377); stdout.printf("0xFF = %d\n" , 0xFf); return 0; }
このプログラムは、10 進数以外の方法による整数リテラルの表記で値を表示するプログラムです。 0b11111111 も 0377 も 0xFf も、すべて 10 進数の 255 と同じ値です。 実行結果を見れば、各進数表記が適切に処理されていることを確認できるでしょう。
浮動小数点数リテラルの扱いも、基本的には C 言語と同じです。 浮動小数点数は、整数部、小数部、指数部に別れ、それぞれ次のように表記しなければなりません。
整数部 . 小数部 e 指数部
整数部と小数部は、いずれかを省略することができます。 ここまでは C 言語と同じですが、D 言語ではさらに 16 進数表現の浮動小数点数リテラルをサポートし、16進数表記の指数を記述することもできます。 16 進数の浮動小数点リテラルは 0x に続いて記述し、指数部は p または P に続いて指定します。
import std.stream; int main() { stdout.printf("3.14 = %g\n" , 3.14); stdout.printf(".314e1 = %g\n" , .314e1); stdout.printf("0xFF.50p-1 = %g\n" ,0xFF.50p-1 ); return 0; }
このように、D 言語では 16 進数を使って浮動小数点数リテラルを表現することができます。 もちろん、浮動小数点数リテラルもアンダーライン _ で数字を区切ることができます。 大きな数を扱う場合に、利用すると便利かもしれません。