文字列

D の文字列は自身の長さを認識していたり, ただの文字の配列であったりするなど, C の文字列とは大きな違いがある.

文字列リテラル

二重引用符文字列:

"hogo"
"Hoge" "Hoge" // 連結される

WYSIWYG 文字列 (r”“で囲むとエスケープシーケンスが使えず, そのままの文字として扱われる):

r"c:\windows"

バッククォート (`) で囲んでも同じだが ” が使える:

`<a href="http://www.apple.com">
Apple Corp.
</a>`

デリミタ指定文字列 (下記のいずれかが使える):

q"()"
q"[]"
q"<>"
q"{}"

トークン文字列 (D のトークンとして正しいものだけを記述できる. コメントも文字列になる):

q{ foo } // "foo"
q{ /+}+/ } // "/+}+/"

文字リテラル

シングルクォーテーション(‘):

'a'
'\n'

文字コード

D は 3 種類の文字コードの文字列を組み込みで持っている.

str_code.d

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import std.stdio;

void main(){
  string a = "hello"; // "hello"c
  wstring b = "HeLLo"; // "HeLLo"w
  dstring c = "HELLO"; // "HELLO"d

  writeln(a, " ", b, " ", c);

  writefln("a.length = %d", a.length);
  writefln("b.length = %d", b.length);
  writefln("c.length = %d", c.length);
}

str_code.d の実行結果は:

[cactus:~/code_d/d_tuts]% ./str_code
hello HeLLo HELLO
a.length = 5
b.length = 5
c.length = 5

文字列リテラルの型を明示するときは次の postfix を付ける.

_images/str_code.png

文字列の長さは .length プロパティで取得

文字の配列としての文字列

今まで何度も使ってきた string は, 実は, immutable(char)[] の alias であるため, 次の 2 行は同じ意味である:

string a = "hoge";
immutable(char)[] a = "hoge";

D の文字列は, ただの文字の配列なので, 配列に対して行える操作のほとんどがそのまま行える.

str_array.d

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import std.stdio;

void main(){
  char[] a = "japan".dup;
  
  a ~= "ese";
  
  a[0..1] = "J";
  
  writeln(a);
}

str_array.d の実行結果は:

[cactus:~/code_d/d_tuts]% ./str_array
Japanese

D の文字列は char, wchar, dchar の (書き換え可能, immutable, const のいずれかの) 配列

immutable(char)[] 型である文字列リテラルを char[] 型の変数で受け取ることはできないので, .dup で char[] 型のコピーを作る必要がある.

ディープコピーとシャローコピー

D では char[] を immutable(char)[] に代入することができないので, copy-on-write がタイプシステムによって強制され保証される.

これによって, 文字列のコピーの無駄が少なくなる.

str_copy.d

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import std.stdio;

void main(){
  string a = "hoge";
  string b;
  b = a; // シャローコピー (参照だけをコピーするので実体は同じものを指している)

  char[] c = a.dup; // .dup プロパティでディープコピー (別の実体を作成してそれを参照)
  char[] d = c; // シャローコピー (参照だけをコピー)

  c[0] = 'H';

  writeln(a);
  writeln(b);
  writeln(c);
  writeln(d);
}

str_copy.d の実行結果は:

[cactus:~/code_d/d_tuts]% ./str_copy
hoge
hoge
Hoge
Hoge

c[0] への代入の結果が同じ実体を参照している d には 影響しているが, 別の実体を持つ a と b には影響していない.

D 言語の配列 (文字列を含む) は参照型

同じ文字列を参照する複数の変数の 一つから文字列を書き換えると, 他のすべての変数の文字列も書き換わる.

普段は string を使い, 書き換え可能な文字列が欲しいときに .dup プロパティでディープコピーする. char[] は immutable(char)[] に代入できないので copy-on-write がタイプシステムによって保証される.

逆に, 書き換え可能な配列から immutable な配列を作るために, .idup プロパティを用いて immutable 型のコピーを作ることができる:

char[] s = "hoge".dup;
immutable(char)[] t = s.idup;

C の関数との連携

cfunc_d.d

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import std.stdio;
import std.string;

void cFunc(const char* s){ // C の関数
  std.stdio.printf("%s\n", s);
}

void main(){
  {
    // 方法1: 標準ライブラリを使う
    string dstr = "hoge";
    const char* cstr = std.string.toStringz(dstr);
    cFunc(cstr);
  }
  {
    // 方法2: '\0' を末尾に追加してから char* にキャストする
    string dstr = "hoge";
    const char* cstr = cast(char*)(dstr ~ '\0');
    cFunc(cstr);
  }
  {
    // 方法3: '\0' を末尾に追加してから .ptr プロパティを使う
    string dstr = "hoge";
    const char* cstr = (dstr ~ '\0').ptr;
    cFunc(cstr);
  }
}

cfunc_d.d の実行結果は:

[cactus:~/code_d/d_tuts]% ./cfunc_d
hoge
hoge
hoge