文字の長さを測るときの注意点を書きます。
Text.Length
Text.Length(
text as nullable text
) as nullable number
Text.Lengthは、文字列の長さを測ります。
Text.Length("Hello World")
この答えは11です。
utf8mb4
英数字はそのまま数えれば答えが出せます。
Text.Length("Hello 太郎")
この答えは8。日本語が混じっていても期待通りの長さになります。しかし、「😇」のような絵文字や「禰󠄀」のような異字体については、期待した長さになりません。
文字 | コードポイント | 符号 | Text.Length | Binary.Length | Power Query |
---|---|---|---|---|---|
A | U+0041 | 41 | 1 | 1 | #(0041) |
あ | U+3042 | E38182 | 1 | 3 | #(3042) |
㉑ | U+3251 | E38991 | 1 | 3 | #(3251) |
😇 | U+1F607 | F09F9887 | 2 | 4 | #(0001F607) |
禰󠄀 | U+79B0 U+E0100 | E7A6B0F3A08480 | 3 | 7 | #(79B0,000E0100) |
「😇」はUnicodeの32ビットで表され、「禰󠄀」については、Unicode16ビットの「禰」の文字に異体字セレクタ32ビットの U+E0100 を付加する形で1文字が作られています。Text.Lengthは16ビットで長さ1と計算しており、「😇」は長さ2、「禰」は長さ3です。Power Queryのコード上でエスケープシーケンスを使って書くと "#(0001F607)" と "#(79B0)#(000E0100)" で表示できます。
面白いことに、Power BIのエディター上では「禰󠄀」の文字を表示させ、バックスペースを一回押すと「禰」の文字に変わります。「禰」(長さ1)+異字体セレクタ(長さ2)の2文字として認識しているようです。
結局「禰󠄀豆子」は「禰󠄀」が長さ3で「豆子」が長さ2となり、合計で長さ5となりました。
文字 | Text.Length | Binary.Length | 符号 |
---|---|---|---|
禰󠄀豆子 | 5 | 13 | E7A6B0F3A08480E8B186E5AD90 |
わかったこと
- Text.Lengthで文字の長さを調べるとき、正しく測れない文字があるので注意
- Power Queryで文字をエスケープシーケンスを使って書くには、Unicodeのコードポイントが0000~FFFFの場合は #(0000) と囲って書く。それより大きいコードは #(00000000) と書く。
- 異字体セレクタなどの複数のコードでできている場合は、#(0000)#(00000000) あるいは #(0000,00000000) のようにして書く。
- 文字をバイナリで分割できるので、文字数をカウントする関数がかけるかもしれない
注意すること
- Power Queryの「Text.Lengh」やDAXの「LEN」などの関数で文字列の長さを算出する際、Unicodeでは見た目の文字の長さと算出された値が異なることがある
- Dataverseなどのデータベースで文字列の長さを厳密に指定する場合、期待した動作をしないことがある
特に2つめは気をつける必要があります。
Sample Code
let
Char = "禰󠄀",
TextLen = Text.Length(Char), // Text.Length = 3
BinLen = Binary.Length(Text.ToBinary(Char)), // Binary.Length = 7
BinList =
List.Transform(
Binary.ToList(Text.ToBinary(Char)),
each Number.ToText(_,"X")
),
HexCode = Text.Combine(BinList)
in
HexCode // Hex Code = E7A6B0F3A08480
(Char as text) as text =>
let
Step1 = Binary.ToList(Text.ToBinary(Char)),
Step2 = List.Accumulate(
Step1,
0,
(state, current) => state * 0x100 + current
),
Step3 = Step2 - 0xE08080,
Step4 =
{
Number.BitwiseAnd(Step3, 0xF0000) / 0x10000,
Number.BitwiseAnd(Step3, 0xFF00) / 0x100,
Number.BitwiseAnd(Step3, 0xFF)
},
Step5 = List.Accumulate(
Step4,
0,
(state, current) => state * 0x40 + current
),
Step6 = Number.ToText(Step5, "X")
in
Step6
(char as text) as text =>
let
Result =
Binary.ToText(
Text.ToBinary(
char,
TextEncoding.BigEndianUnicode
),
BinaryEncoding.Hex
)
in
Result