7

Delphi XE8 ヘルプの引用:

シングルバイト文字列とマルチバイト文字列の場合、Length は文字列で使用されるバイト数を返します。UTF-8 の例:

   Writeln(Length(Utf8String('1¢'))); // displays 3

Unicode (WideString) 文字列の場合、Length はバイト数を 2 で割った値を返します。

これにより、重要な質問が発生します。

  1. ハンドリングに違いがあるのはなぜですか?
  2. Length() が期待どおりに動作せず、場合によってはサイズをバイト単位で指定するのではなく、パラメーターの長さ (要素の数など) だけを返すのはなぜですか?
  3. Unicode (UTF-16) 文字列の結果を 2 で除算すると述べているのはなぜですか? AFAIK UTF-16 は最大で 4 バイトであるため、これにより誤った結果が得られます。
4

1 に答える 1

12

Length文字列を配列と見なした場合の要素数を返します。

  • 8 ビット要素タイプ (ANSI、UTF-8) の文字列Lengthの場合、バイト数は要素数と同じであるため、バイト数が得られます。
  • 16 ビット要素 (UTF-16) を持つ文字列の場合Length、各要素が 2 バイト幅であるため、バイト数は半分になります。

文字列 '1¢' には 2 つのコード ポイントがありますが、2 番目のコード ポイントを UTF-8 でエンコードするには 2 バイトが必要です。したがってLength(Utf8String('1¢'))、3 に評価されます。

SizeOfあなたは質問のタイトルに言及しています。文字列変数を に渡すと、SizeOf常にポインターのサイズが返されます。これは、文字列変数は内部的には単なるポインターであるためです。

あなたの特定の質問に:

ハンドリングに違いがあるのはなぜですか?

Lengthバイトに関連すると考える場合にのみ違いがあります。しかし、それは常に要素数を返すと考えるのは間違った方法Lengthです。そのように見ると、すべての文字列型、実際にはすべての配列型で動作が均一です。

Length() が期待どおりに動作せず、場合によってはサイズをバイト単位で指定するのではなく、パラメーターの長さ (要素の数など) だけを返すのはなぜですか?

常に要素数を返します。要素のサイズが 1 バイトの場合、要素の数とバイトの数がたまたま同じになることがあります。実際、参照しているドキュメントには、提供した抜粋のすぐ上に次の記述も含まれています。文字列内の文字数または配列内の要素数を返します。それがキーテキストです。あなたが含めた抜粋は、このイタリック体のテキストの意味を説明するためのものです。

Unicode (UTF-16) 文字列の結果を 2 で除算すると述べているのはなぜですか? AFAIK UTF-16 は最大で 4 バイトであるため、これにより誤った結果が得られます。

UTF-16 文字要素は常に 16 ビット幅です。ただし、一部の Unicode コード ポイントでは、エンコードに 2 つの文字要素が必要です。これらの文字要素のペアは、サロゲート ペアと呼ばれます。


Length文字列内のコードポイントの数を返すことを望んでいると思います。しかし、そうではありません。文字要素の数を返します。また、可変長エンコーディングの場合、コード ポイントの数は必ずしも文字要素の数と同じではありません。文字列が UTF-32 としてエンコードされている場合、UTF-32 は一定サイズのエンコードであるため、コード ポイントの数は文字要素の数と同じになります。

コード ポイントをカウントする簡単な方法は、文字列をスキャンしてサロゲート ペアをチェックすることです。サロゲート ペアに遭遇したら、1 つのコード ポイントを数えます。それ以外の場合、サロゲート ペアの一部ではない文字要素に遭遇した場合は、1 つのコード ポイントを数えます。擬似コード:

N := 0;
for C in S do
  if C.IsSurrogate then
    inc(N)
  else
    inc(N, 2);
CodePointCount := N div 2;

もう 1 つの注意点は、コード ポイント数が表示可能な文字数と同じではないということです。一部のコード ポイントは結合文字であり、隣接するコード ポイントと結合されて、1 つの可視文字またはグリフを形成します。

最後に、文字列ペイロードのバイト サイズを確認することだけが目的の場合は、次の式を使用します。

Length(S) * SizeOf(S[1])

この式は、すべてのタイプの文字列で機能します。

関数には十分注意してくださいSystem.SysUtils.ByteLength。一見すると、これはまさにあなたが望むもののようです。ただし、その関数は UTF-16 でエンコードされた文字列のバイト長を返します。したがってAnsiString、たとえば を渡すと、 によって返される値はByteLengthのバイト数の 2 倍になりますAnsiString

于 2015-06-03T12:16:34.627 に答える