[質問]ヌル文字をちゃんと1文字として扱うNo.09427
fzok4234 さん 21/06/01 12:25
 
こんにちは。

さて、ヌル文字"\U00000000"の扱いについてですが、現状ではこれの存在を無視して
空文字列@""と同等に
扱われているみたいです。これをれっきとした「意味のある1文字」として扱いたい
場合にはどうすれば
よいのでしょうか。

というのは、文字列を外部DLLに渡す際に、そのDLLでヌル文字もちゃんと「意味のあ
る1文字」として
扱うことがあるからです。特に、.NETのアセンブリにて文字列System.Stringやその
要素System.Charでは
ヌル文字"\U00000000"もちゃんと「意味のある1文字」とする厳格な扱いとなってい
ます。

もし現状のマクロ構文でこれが不可能ならば、
・setcompatiblemodeでヌル文字"\U00000000"を厳格に1文字として扱うモードに切り
替え可能にする。
などの機能追加をお願いしたいところです。

どうかよろしくお願いします。

なお、検証マクロは以下の通りです。

debuginfo 2 ;
debuginfo str( @""          == "\U00000000" )            + "\U0000000A" ; //
 1。
debuginfo str( "\U00000000" == "\U00000000" )            + "\U0000000A" ; //
 1。
debuginfo str( @"a" == ( @"a"         + "\U00000000" ) ) + "\U0000000A" ; //
 1。
debuginfo str( @"a" == ( "\U00000000" + @"a"         ) ) + "\U0000000A" ; //
 1。
debuginfo ( @"a" + "\U00000000" )                        + "\U0000000A" ; //
 a。
debuginfo ( "\U00000000" + @"a" )                        + "\U0000000A" ; //
 a。
debuginfo str( ucs4len( @""          ) )                 + "\U0000000A" ; //
 0。
debuginfo str( ucs4len( "\U00000000" ) )                 + "\U0000000A" ; //
 0。
debuginfo str( ucs4len( @"a"         + "\U00000000" ) )  + "\U0000000A" ; //
 1。
debuginfo str( ucs4len( "\U00000000" + @"a"         ) )  + "\U0000000A" ; //
 1。
endmacro ;


[ ]
RE:09427 [質問]ヌル文字をちゃんと1文字No.09430
こみやんま さん 21/06/01 15:55
 
それは、.NETがヌルを終端文字としていないからで、
C層のAPIでは、ほぼことごとくヌルが終端文字なので(そもそも管理されたバイト配
列扱いではなく大抵先頭のメモリアドレスでしかやりとりしていないのだから...)、
すくなくとも文字列扱いでありながら、ヌルを終端文字としないモードの実装はいま
からの秀丸マクロだと厳しい気がしますよ。

C#でも、少しでもC系(=ヌル文字が終端として取り扱われている層)を匂わせるような

class Program
{
    unsafe static void Main(string[] args)
    {
        String a = "あ\0";
        Console.WriteLine(a.Length);

        fixed (char* p = a)
        {
            String d = new String(p);
            Console.WriteLine(d.Length);
        }
    }
}

みたいに記述するだけでヌル以降は吹き飛んでしまうので。
先頭アドレスしか持たない機構を介する場合、C#であったもヌル文字はやはり終端文
字である、
といえます。

ただ、オプション切り替えると、文字列の中のヌルをそのまま〜とかではなく(既存
の文字列系関数が全く対応できないからそれ)、
現在のところ秀丸マクロには、「バイト列」をまともに扱う機構が
ないので、「バイト列型変数」みたいなのを「新たなシンボルとともに導入」するのは
個人的にはギリギリでありかもねーとは思います。
(現在 allowobjparam 2; が少しかすってる程度かな?)


この場合、dll系の呼び出し関数(dllfunc系)に追加する場合でも、「返り値としてバ
イトデータを受け取る」場合は、
がどうしても2つの情報(データと長さ)が必要なので、
引数が参照(もしくはポインタ)=out(or ref)であるという参照として渡す機構も
必要な気がします。
(makeref とかを新設すればきっと可能な気もします。)

こういった意味では、現在、秀丸の特定の関数(=getlinecount)などは、
特定の引数を内部で勝手に参照扱いしてout先にしているようですが
本来は何か参照(out先)であることの印を付けるような機構を足してから
それを使って実装したほうがよかったんでないのかなそれ、とは思いましたね。

[ ]
RE:09430 [質問]ヌル文字をちゃんと1文字No.09431
秀丸担当 さん 21/06/01 17:24
 

ヌル文字を1文字として扱うようにできるのは、結構無理があって、実現するのは難
しいと思います。
[その他]→[動作環境]→[ファイル]→[エンコード2]に「NULL文字の変換」があって
本文中に特殊文字として置く方法もありますが、空白か途中で切れるかの選択しかな
いです。
もしもうちょっと踏み込んだとしても、コピペなどいろんなところで不都合が出ると
思います。

getlinecountみたいな変数名を参照するような形は文法的に独特だと思います。menu
arrayとかddestartadviceとか、かなり初期のころからちょっと変わった記法という
か解釈の仕方があったりします。

[ ]
RE:09431 [質問]ヌル文字をちゃんと1文字No.09435
fzok4234 さん 21/06/01 17:46
 
> ヌル文字を1文字として扱うようにできるのは、結構無理があって、実現するのは
> 難しいと思います。
> [その他]→[動作環境]→[ファイル]→[エンコード2]に「NULL文字の変換」が
> あって本文中に特殊文字として置く方法もありますが、空白か途中で切れるかの
> 選択しかないです。
> もしもうちょっと踏み込んだとしても、コピペなどいろんなところで不都合が出る
>と思います。

了解しました。

ただ、あまり見たことがないのですが、もしCOMオブジェクトのメソッドが
引数や戻り値に「ヌル文字区切り」の文字列を使用しているときなどはどのように
対処すればよいのでしょうか。




[ ]
RE:09435 [質問]ヌル文字をちゃんと1文字No.09437
こみやんま さん 21/06/01 18:14
 
>ただ、あまり見たことがないのですが、もしCOMオブジェクトのメソッドが
>引数や戻り値に「ヌル文字区切り」の文字列を使用しているときなどはどのように
>対処すればよいのでしょうか。
>
それは文字列として認識するよりもバイト列として認識するべきだと思います.

結構レアなハズ(もしくはそもそも製作者以外の第3者が直接取り扱うことを前提と
していない確率高い)なので、
C++なりC#なりでバイト列として読み込んで、配列的な形なり、ハッシュ的な形なり
でGetメソッドを作り、秀丸マクロの「文字列」と「数値」のマクロ空間から引っ張
れるようにする、
でいいと思いますよ。
(秀丸マクロからそれを使いたいのであれば・・・ですが)

自作のdllやcomに対応してくれてるのは「レアな要求は本格的な言語を使って実装を
満たせ」という意味あいも強いでしょうから、

[ ]
RE:09437 [質問]ヌル文字をちゃんと1文字No.09438
fzok4234 さん 21/06/01 20:42
 
一応、C#の文字列を返すメソッドにて
@"a" + "\0" + @"b" + "\0\0" + @"c"
を生成して、COMとhm.NET.dllとの両方で受け取ってみました。

// ---------------- NullDelimited.dll ----------------
using System.Runtime.InteropServices ;
using System ;

[ GuidAttribute( @"a7e093cd-353a-482b-8030-ffc95eb93f3f" ) ]
[ InterfaceType( ComInterfaceType.InterfaceIsIUnknown ) ]
public interface INullDelimited {
  String GetNullDelimitedString() ;
}

[ GuidAttribute( @"296f5def-a494-4c5b-9f1e-c9b6570a5542" ) ]
[ InterfaceType( ComInterfaceType.InterfaceIsIDispatch ) ]
public interface INullDelimitedEvent {}

[ GuidAttribute( @"133082d8-d63f-4de0-a5c4-985bd97fbe09" ) ]
[ ClassInterface( ClassInterfaceType.AutoDual ) ]
[ ComSourceInterfaces( typeof( INullDelimitedEvent ) ) ]
public class NullDelimited : INullDelimited {
  public String GetNullDelimitedString() {
    return @"a" + "\0" + @"b" + "\0\0" + @"c" ;
  }
}

public static class NullDelimitedStatic {
  private static NullDelimited nullDelimited = default( NullDelimited ) ;
  static NullDelimitedStatic() {
    nullDelimited = new NullDelimited() ;
  }
  public static String GetNullDelimitedString() {
    return nullDelimited.GetNullDelimitedString() ;
  }
}

// ---------------- NullDelimited.dll ----------------

// ---------------- NullDelimited.mac ----------------
debuginfo 2 ;
$nullDelimitedDllPath = currentmacrodirectory + @"\NullDelimited.dll" ;
#nullDelimitedCom = createobject( $nullDelimitedDllPath , @"NullDelimited" ) ;
  call Dump @"COM" , callmethod_returnstr( #nullDelimitedCom , @"GetNullDeli
mitedString" ) ;
releaseobject #nullDelimitedCom ;
#hmDotNet = loaddll( hidemarudir + @"\hm.NET.dll" ) ;
call Dump @"DotNET" , dllfuncstrw( #hmDotNet , @"CallMethod" , $nullDelimite
dDllPath , @"NullDelimitedStatic" , @"GetNullDelimitedString" ) ;
endmacro ;

Dump :
  debuginfo $$1 + @"_Value  = " + $$2 + "\U0000000A" ;
  debuginfo $$1 + @"_Length = " + str( ucs4len( $$2 ) ) + "\U0000000A" ;
  return ;

// ---------------- NullDelimited.mac ----------------

すると、実行結果は以下のようになりました。

COM_Value  = a
COM_Length = 1
DotNET_Value  = a
DotNET_Length = 1

COMとhm.NET.dllとの両方において、ヌル文字"\0"以降がものの見事にカットされて
しまいました。
ちなみに、Windows PowerShellで

Add-Type -LiteralPath '.\NullDelimited.dll'
$s = [NullDelimitedStatic]::GetNullDelimitedString()
$s
$s.Length

とすると、結果は

a b  c
6

というヌル文字"\0"を厳格に1文字として扱う結果となっています。このことから、
秀丸エディタ側で
ヌル文字"\0"以降のカットが行われているものとみられます。


[ ]
RE:09438 [質問]ヌル文字をちゃんと1文字No.09439
こみやんま さん 21/06/01 21:32
 
>というヌル文字"\0"を厳格に1文字として扱う結果となっています。このことから、
>秀丸エディタ側で
>ヌル文字"\0"以降のカットが行われているものとみられます。

秀丸マクロというか、wchar_t* や BSTR あたりが、経由してしまうので
そこで事実上カットが起こります。

PowerShellでカットされないのは、Powershellは.NETの拡張REPLだから...

そこで「\0」付きで「1つにして返さなきゃ!」 みたいなことにこだわる必要はなく、
C#層で「\0」なりでsplitして、Dictinary<T, string>でもList<string>でもなんで
もいいですが格納し、
普通に、Get***Data(int ix) なり Get***Data(string key) なりの形になおせばい
いじゃないということです。

[ ]
RE:09439 [質問]ヌル文字をちゃんと1文字No.09440
fzok4234 さん 21/06/01 21:46
 
そういえば、.NET Frameworkの何かのメソッドで"\0"区切りの文字列をやり取りする
ものが
あったような...。
もしそういう「レア」なケースに遭遇したら、おっしゃる通り「ラッパー」を作って
応戦しようと思っています。


[ ]