11
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Power Query で「禰󠄀豆子」の長さを測る

Last updated at Posted at 2022-09-09

 文字の長さを測るときの注意点を書きます。

Text.Length

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
文字からコードポイントを表示1(コードポイント0800~FFFF)
(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
文字からコードポイントを表示2(コードポイント0800~FFFF)
(char as text) as text =>
let
    Result =
        Binary.ToText( 
            Text.ToBinary(
                char, 
                TextEncoding.BigEndianUnicode
            ),
            BinaryEncoding.Hex
        )
in
    Result
11
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?