文字の数え方についてNo.09234
fzok4234 さん 20/07/21 13:00
 
お世話になっております。

さて、初歩的な質問なのですが、以下に示すマクロ上での文字の「数え方」があまり
よくわかりません。ご教示よろしく
お願いします。

1.  カラム位置や文字数の取得/指定について。
    具体的には、秀丸エディタ上のカラム位置のcolumnキーワードや全文字数のchar
count()関数、マクロ上の文字列式の
    長さを表すwcslen()関数、およびこれらの計算方法に準じた文字数指定を伴う全
ての処理において、以下の場合に
    どのように扱われるのかを前もって知っておきたいのです。
   
    1.1 サロゲートペア。
    2.1 異字体セレクタ。
    3.1 合成済み文字。
    4.1 BOM。
    5.1 改行コード(「\rまたは\n単独」と「\r\n」との文字数の違い)。
    6.1 C/C++上の文字列の終端のNull文字(U+0000)。
    7.1 その他制御コード。

2.  秀丸エディタ上の位置(( x , y )または( column , lineno )の組み合わせ)と、
先頭からの合計文字数(改行文字等も
    全て含む)との相互変換を、マクロ上でwhileループを回すことなくできるだけ高
速に行いたいのですが、ヘルプを
    見た限り該当する機能の関数などが見つかりませんでした。特定の範囲の文字列
を先頭からの位置と長さで指定して
    その起点と終点の( column , lineno )を取得してcolormarkerで色付けする、と
いった運用を考えています。



[ ]
RE:09234 文字の数え方についてNo.09235
秀丸担当 さん 20/07/21 15:35
 

wcslenは、UTF-16単位で数えられます。
詳細は以下のような感じですが、UTF-16単位で難しいことはないと思います。

サロゲートペアは2になります。
異体字セレクタは、U+FE00〜U+FE0Fであれは+1文字、U+E0100〜U+E01EFであればサロ
ゲートペアなので+2文字として数えられます。
合成済み文字は普通のUnicode1文字であればUnicode1文字です。
BOMは文字列として表すことはできますが、エディタ本文には存在しないです。
改行コードは文字列として表すことはできますが、columnに数えらえることは無いで
す。
Null文字は、文字列として表すことはできません。
その他制御コードは1です。


columnは、文字の幅が影響していて、UTF-16の一文字(UCS-2)であっても、半角相
当であれば1、全角相当であれば2として数えられたりします。
wideindex_to_charindex関数で、wcslen相当の数え方から、columnの数え方に変換す
ることができます。

実はV8.90まではフォントの持つ幅情報に依存していて、本文とフォントが無いとわ
からないことでした。
V8.90以降でも文字の幅によって違うのですが、フォントに関わらず、常に固定の判
断になっています。(漢字は必ず2、アルファベットは必ず1、とか)
漢字やアルファベットじゃない文字は固定ではあるものの、まちまちなので、どのみ
ち実際にcolumnを見たり、wideindex_to_charindex関数で変換したりしないとわから
ないです。
サロゲートペア、異体字セレクタ、合成済み文字も同様に実際に見たり変換しないと
わからないです。
BOMはエディタ本文には存在しないです。
改行コードはcolumnに数えらえることは無いです。
Null文字は、[その他]→[動作環境]→[ファイル]→[エンコード2]の、「NULL文字の
変換」によります。通常は空白に変換されて読み込まれます。特殊文字は、どこかで
何かしらの不都合が起きる可能性があると思います。
その他制御コードは1です。


[ ]
RE:09235 文字の数え方についてNo.09236
fzok4234 さん 20/07/21 17:57
 
詳細な説明ありがとうございます。


追加でいくつか質問がございます。

まず、wcslen()において、

> 合成済み文字は普通のUnicode1文字であればUnicode1文字です。

とあるのですが、「普通でない」文字すなわち複数のスカラー値からなる「書記素ク
ラスタ」は正しく構成スカラー値の
サイズの合計となるのでしょうか?

例えば、「女性消防士」を表す絵文字は
    U+1F469  (サロゲートペア)  +
    U+1F3FD  (サロゲートペア)  +
    U+200D   (通常スカラー値)  +
    U+1F692  (サロゲートペア)
となっているのですが、wcslen()はこれを正しく 2 + 2 + 1 + 2 = 7文字と返してく
れるのでしょうか?


また、同じ文字が複数の種類の異なるコードポイントで表現可能なときにはwcslen()
はどのように振る舞うのでしょうか?

例えば、「ắ」という文字は
    U+1EAF
    U+0103  +  U+0301
    U+0301  +  U+0306  +  U+0301
の3種類で表現可能ですが、スカラー値のサイズの合計も上から1文字、2文字、3文字、
と異なっているのですが、wcslen()は
この3つのどれを採用するのでしょうか?


それから、秀丸エディタ上の「指定範囲」の文字列を表す
    1.  秀丸エディタの先頭から指定範囲の開始点までの文字数
    2.  指定範囲の長さ
の2つの値を、colormarker文などで範囲指定に必要な
    1.  指定範囲の開始点のcolumn
    2.  指定範囲の開始点のlineno
    3.  指定範囲の終了点のcolumn
    4.  指定範囲の終了点のlineno
の4つの値に変換したり、あるいはこの逆の変換を行うにはどうすればよいのでしょ
うか?

現状では、先頭から指定範囲の位置までの各行数を順番に加算または減算して導出す
る必要があるのですが、「指定範囲」の中に
改行コードが含まれている場合にはその改行コードが何文字として扱われるかを予め
把握しておかないといけません。



[ ]
RE:09236 文字の数え方についてNo.09237
でるもんたいいじま さん 20/07/21 19:40
 
こんにちは。秀丸愛用者の「でるもんた・いいじま」です。

> 「普通でない」文字すなわち複数のスカラー値からなる
> 「書記素クラスタ」は正しく構成スカラー値のサイズの
> 合計となるのでしょうか?
>
> 例えば、「女性消防士」を表す絵文字は
>     U+1F469  (サロゲートペア)  +
>     U+1F3FD  (サロゲートペア)  +
>     U+200D   (通常スカラー値)  +
>     U+1F692  (サロゲートペア)
> となっているのですが、wcslen()はこれを正しく 2 + 2 + 1 + 2 = 7文字と
> 返してくれるのでしょうか?

実験してみました。実際に7になります。

$a = unichar(0x1F469) + unichar(0x1F3FD) + unichar(0x200D) + unichar(0x1F692);
message sprintf("wcslen('%s') == %d\n", $a, wcslen($a));

あるいは、こんなテストコードに実際に入力してみることもできます。

while (1)
{
 $a = input("Enter some text.");
 if ( !result ) endmacro;
 message sprintf("wcslen('%s')==%d", $a, wcslen($a) );
}

☆ ☆ ☆

> また、同じ文字が複数の種類の異なるコードポイントで表現可能な
> ときにはwcslen()はどのように振る舞うのでしょうか?

これは、
「秀丸はコードの正規化を行わない」
「バイナリ表現が異なれば、意味するものが同一でもそれぞれ異なる文字列として扱
う」
というのが正解になると思います。

なので、

> 例えば、「ắ」という文字は
>     U+1EAF
>     U+0103  +  U+0301
>     U+0301  +  U+0306  +  U+0301
> の3種類で表現可能ですが、スカラー値のサイズの合計も
> 上から1文字、2文字、3文字、と異なっているのですが、
> wcslen()はこの3つのどれを採用するのでしょうか?

この例でいえば、wcslenはこのそれぞれのコード列に対してそれぞれ異なる値(つま
り、順に1、2、3)が返ります。

$a = unichar(0x1EAF);
$b = unichar(0x0103) + unichar(0x0301);
$c = unichar(0x0101) + unichar(0x0306) + unichar(0x0301);
message sprintf("[%s]:%d\n[%s]:%d\n[%s]:%d\n",
  $a, wcslen($a), $b, wcslen($b), $c, wcslen($c));

また、この3つの文字列を等号で比較したり、strstr() でマッチングさせたりしても、
内部表現が異なっていれば同じ文字列とはみなされません。

$a = unichar(0x1EAF);
$b = unichar(0x0103) + unichar(0x0301);
message sprintf("($a==$b)==%d, strstr($a,$b)=%d\n", ($a==$b), strstr($a,$b));

検索でも同様です。
setcompatiblemode 0x20000;
$a = unichar(0x1EAF);
$b = unichar(0x0103) + unichar(0x0301);
insert sprintf( "Here is the patterns: '%s' and '%s'.\n", $a, $b);
searchdown "[\\u1EAF]", regular, hilight, loop;

#ところで3番目のパターン、最初のU+0301はU+0101か何かの間違いですよね?
#合成用文字だけが3つ並んでいて、そのベースになる文字がありませんので。

☆ ☆ ☆

もし unichar() では心配ということであれば、マクロファイルを「UTF-16LEのBOMつ
き」または「UTF-8のBOMつき」で保存すれば大抵のものは文字列の中にそのまま書け
るはずですので、ぜひそれでテストコードを色々と書いてみてください。

☆ ☆ ☆

後半の件につきましては担当さんからのお返事をお待ちください。

[ ]
RE:09236 文字の数え方についてNo.09238
fzok4234 さん 20/07/21 20:35
 
-PS-


> それから、秀丸エディタ上の「指定範囲」の文字列を表す
>     1.  秀丸エディタの先頭から指定範囲の開始点までの文字数
>     2.  指定範囲の長さ
> の2つの値を、colormarker文などで範囲指定に必要な

ここのところに関する大事な質問を忘れていました。それは、HmJre.dllやその互換
正規表現エンジンにおける
    GetLastMatchTagPosition()  関数
    GetLastMatchTagLength()    関数
が返す位置と長さにおける文字数の数え方についてですが、
    1.  wcslen()相当のUTF-16のデータ単位
    2.  column相当の文字の見かけの幅単位
のどちらが適用されるのか? という事でした。

当方では、強調表示する文字列にマッチするタグ付き正規表現で秀丸エディタ上の全
文をFindRegular()相当の
関数で1回だけ検索をかけ、マッチした部分の位置をGetLastMatchTagPosition()で、
長さをGetLastMatchTagLength()で
取得し、それを使ってcolormarker文で色付け、という流れでより精度の高い強調表
示の定義作成を検討しています。
そこでネックになるのが、マッチした部分の
    開始位置
    長さ
の2値を、colormarker文の範囲指定で必要な
    開始行
    開始桁
    終了行
    終了桁
の4値に変換する作業と、それに伴う前者と後者とでの文字の数え方の違いの吸収作
業です。

いずれも、ヘルプ上などでの情報が乏しかったため、今回の質問となった次第であり
ます。どうかよろしくお願いします。



[ ]
RE:09237 文字の数え方についてNo.09239
fzok4234 さん 20/07/21 20:49
 
お忙しい中わざわざ詳細な実験ありがとうございます。


マクロにおけるUnicode関係の機能はほぼきっちりUTF-16のデータ単位で行われている
という事がわかりました。本当に助かりました。

一応、当方ではよくC#やPowerShellで.Net Frameworkの「System.String」文字列型を
使っており、厳密にUTF-16単位(System.Char文字型)で文字を管理するSystem.String
型と
秀丸エディタのUnicode機能との間で互換性が取れているのか気になっておりました。

ただ、「正規化の有無」に関してはSystem.String型(というかC#とPowerShell)がど
う扱っているのか
こちらでテストしてみる必要があります。



[ ]
RE:09238 文字の数え方についてNo.09240
秀丸担当 さん 20/07/22 11:18
 

カラー絵文字や、正規化は行わないのは、でるもんたいいじまさんの言われる通りで
す。
ありがとうございます。

HmJre.dllのFindRegular()等の関数の数え方は、byteindex_to_charindex、charinde
x_to_byteindexの変換関数があります。
参考:HmJre.dllのヘルプ(Web版)
https://help.maruo.co.jp/hmjre/html/0008_API_MACRO.html

改行については、columnには数えられないです。
ファイル先頭からの位置、あるいは複数行文字列の先頭からの位置で、1つの位置の
値を行と桁に分けて一発で変換する方法は無いです。
改行の出現回数をカウントするなどをして、行を算出してから、その行頭からの桁位
置で変換してやるしかないと思います。
ちょっと面倒かもしれません。

[ ]
RE:09240 文字の数え方についてNo.09241
fzok4234 さん 20/07/27 17:18
 
> HmJre.dllのFindRegular()等の関数の数え方は、byteindex_to_charindex、charin
>dex_to_byteindexの変換関数があります。

ヘルプの内容の見落としにつき大変失礼いたしました。

さて、ヘルプによると、HmJre.dllでやり取りする文字数の扱いは「半角1Byte、全角
2Byte、Unicode4Byte」となっていますが、
HmJre.dll互換の他の正規表現エンジン(hmonig.dllなど)もこの仕様に準拠させるこ
とが必須条件となっているのでしょうか?
それとも、各DLLごとに自由裁量となっているのでしょうか?
DLLごとに仕様がまちまちだと文字数の扱いに大変苦労しそうです。


> ファイル先頭からの位置、あるいは複数行文字列の先頭からの位置で、1つの位置
>の値を行と桁に分けて一発で変換する方法は無いです。
> 改行の出現回数をカウントするなどをして、行を算出してから、その行頭からの桁
>位置で変換してやるしかないと思います。
> ちょっと面倒かもしれません。

個人的な見解なのですが、秀丸エディタはテキストを改行コードを跨いで1本の文字
の1次元配列として扱う機能が大変貧弱である印象を
受けています。マクロ上に限らず通常のGUI操作においても、カーソルの位置は常に
行とカラムの2次元値で扱われており、
    ・ファイルの先頭からの文字数を指定してカーソルを移動させる。
    ・改行を跨いでの文字数指定で範囲選択する。
といった操作がうまく行えません。

また、「表示」->「文字数計算」などの機能もファイルが大きくなると動作が重くな
り、手元で134,217,728文字(1行4096文字*32768行)の
ファイルで実行すると1回あたり1秒ほど時間がかかります。

もし、テキストが内部で1次元配列扱いされているならば、先頭からの絶対位置は配
列のインデックス番号だけで指定できるのでカーソル移動や
文字数計算は簡単にできそうな感じがするのですが、内部仕様がユーザーから見えな
いので機能追加が簡単に実現可能かどうかがわかりません。



[ ]
RE:09241 文字の数え方についてNo.09242
秀丸担当 さん 20/07/27 18:14
 

正規表現DLLの必須条件とかが定められているわけではないですが、hmonigのh-tomさ
んはUnicode4バイトのいわゆる秀丸独自コードの対応も考慮していただけているよう
です。

でも秀丸エディタの検索や強調表示を行うための関数と、dllfuncから呼び出す関数
は全く別の存在になっています。
秀丸エディタの検索は、山田和夫さん作のjre32.dllが元になっている関数群の延長
にあって、Jre2Open、JreGetMatchInfoといった関数とその拡張になっています。
(https://hide.maruo.co.jp/software/bin3/hmjreheader507.zip)
秀丸独自コードの対応をしていただけているのはこちらのほうだと思います。

FindRegular()等のdllfunc用の関数は、本当にdllfunc用で、秀丸エディタ本体から
呼ばれることは想定されていないです。
dllfuncの関数は、秀丸独自コードは渡さずにUnicodeは'?'などの文字に変換されて
渡してしまいます。秀丸独自コードが通用するのはHmJre.dllだけです。
hmonigを使う場合は、FindRegularW()といった関数があるようなので、これをdllfun
cではなくdllfuncwで使ったほうがいいと思います。
FindRegularW()はhmonigの独自の関数で、HmJre.dllにはFindRegularW()は無いです。
つまり、DLLごとに別々に考えるという、面倒なことをする必要があることになって
しまいます。

1つの位置の値から、改行をまたいでの行と桁にするのは面倒で、弱いかもしれませ
ん。すみません。
1つ関係することとして、gettextやgettext2は、改行を\r\nにして取得するという
ことがあります。
HmJre.dllのヘルプにはReplaceRegularで\r\nを\nに置換するといいということにな
っていますが、最初から\nで取得できたらいいので、そういうパラメータも追加しよ
うと思います。

[ ]
RE:09241 文字の数え方についてNo.09243
h-tom さん 20/07/27 21:11
 
h-tom です。

>さて、ヘルプによると、HmJre.dllでやり取りする文字数の扱いは「半角1Byte、全角
>2Byte、Unicode4Byte」となっていますが、
>HmJre.dll互換の他の正規表現エンジン(hmonig.dllなど)もこの仕様に準拠させること
>が必須条件となっているのでしょうか?
>それとも、各DLLごとに自由裁量となっているのでしょうか?
>DLLごとに仕様がまちまちだと文字数の扱いに大変苦労しそうです。
担当さんの回答と同じですが、私が関わっている互換dllは以下のような状態です。
・hmonig.dll は、jre/hmjreの検索用API だと、秀丸エディタ独自のUnicode表現に
対応していますが、
  マクロ用関数に関しては、対応していません。ワイド文字対応のマクロ関数を使っ
てください。
・BRegIf.dllは、jre/hmjreの検索用API、マクロ用関数共に対応していません。
  検索用APIの、Shift_JIS範囲外のUnicodeに関しては、JRE32.DLLでもある程度動く
よう考えられていることから、
  動いているだけの状態。

マクロ用関数に関しては、以下も参照の事。
・h-tom's Warehouse - 秀丸エディタ macro DLL
  http://htom.in.coocan.jp/macro/macro_dll.html#N4.6
・第III部〜秀丸マクロのいろはにほへと HMJRE.DLLを使う場合の注意点
  http://htom.in.coocan.jp/hmfaq8/3_hmjre.html

最終的にはマクロを使うことになりますが、解析自体は「Hidemaru_GetTotalTextUni
code」使って内容取り込んで、
DLL内で行った方がいいとは思いますよ。

[ ]
RE:09243 文字の数え方についてNo.09244
fzok4234 さん 20/07/28 03:02
 
回答ありがとうございます。

リンク先を拝見しましたが、サロゲートペアは8Byte扱いになってしまうなど、大変
勉強になりました。素直に
hmonig.dllのFindRegularWやGetLastMatchNameToTagCntWを使用したほうが良いみた
いです。



[ ]