新規記事の投稿を行うことで、非表示にすることが可能です。
2023年03月19日
pascalとJavaScript 関数内関数でハマった
Z変換の周波数特性表示の為に 数式処理をpascalコードからJavaScriptの変換作業をしてるのですが
最初クラスで書いてJavaScriptに変換しようとすると大量のthisに溢れたコードで鬱陶しくなり、これはダメだと 関数内関数に変更しました。
問題は数式処理をreplaceのコールバック関数を使った為にpascal側では関数内関数をコールバック出来ない事。
仕方なくpascal側ではreplace関数をexecで展開して呼ぶように修正。
さて大丈夫かと思ったら、大量の実行エラー。原因はクラスメンバー参照。
Pascal側では関数内関数でもselfはそのクラスを示しているし、メンバー参照も出来るのですが
JavaScriptの関数内関数はthisを自動では引き継いでくれないようです。
func1(param1,param2)の代わりに func1.call(this, param1, param2); てな置換が必要。
そうすると短い関数呼び出しが長くなってやはり鬱陶しい。
関数内関数にした事でメンバー数は減っているので
関数の先頭で z:=self;のように代入して関数内関数からはzを参照するように変更しました。
作ったツールでjavascriptに置換して thisを探して 元のpascalにz.を付ける作業をして全体が動きやっとデバッグが出来る状態に。
他にもAND OR は & | にしておけば論理でもバイナリでも問題ないけど not は ~ だと論理式の場合常にtrueになってしまうとか、なかなか厄介です。(notはif while untilで最初に見つけたら!に変換する事にしたけど)
最初クラスで書いてJavaScriptに変換しようとすると大量のthisに溢れたコードで鬱陶しくなり、これはダメだと 関数内関数に変更しました。
問題は数式処理をreplaceのコールバック関数を使った為にpascal側では関数内関数をコールバック出来ない事。
仕方なくpascal側ではreplace関数をexecで展開して呼ぶように修正。
さて大丈夫かと思ったら、大量の実行エラー。原因はクラスメンバー参照。
Pascal側では関数内関数でもselfはそのクラスを示しているし、メンバー参照も出来るのですが
JavaScriptの関数内関数はthisを自動では引き継いでくれないようです。
func1(param1,param2)の代わりに func1.call(this, param1, param2); てな置換が必要。
そうすると短い関数呼び出しが長くなってやはり鬱陶しい。
関数内関数にした事でメンバー数は減っているので
関数の先頭で z:=self;のように代入して関数内関数からはzを参照するように変更しました。
作ったツールでjavascriptに置換して thisを探して 元のpascalにz.を付ける作業をして全体が動きやっとデバッグが出来る状態に。
他にもAND OR は & | にしておけば論理でもバイナリでも問題ないけど not は ~ だと論理式の場合常にtrueになってしまうとか、なかなか厄介です。(notはif while untilで最初に見つけたら!に変換する事にしたけど)
2023年03月11日
久しぶりに数式処理を書いた
Z変換で周波数特性を表示させるJavaScriptを書くためにPascalで先に書いて動くコードをJavaScriptに直す事にした。
数式処理はGIkoBasicをベースにAddSub/MulDivと再帰下降で書いていたのを手直ししていただけなので
正規表現で分解された要素が引数として与えられて処理する方式となるのは初めてのスタイルだ。
async/await を使えば再帰下降のままでも処理出来そうだが、Pascal側にその機能がないので先にコードが書けない。
四苦八苦してとりあえずpascal側である程度動作するコードは書けた。
Lazarusで正規表現を使う例とかで参考になるかもしれないので現時点でのコードを公開してる=>
z変換で周波数特性を得るjavascript作成奮戦記
これからこの千行付近あるコードをJavaScriptに直すのはちょっと頑張らないといけない
数式処理はGIkoBasicをベースにAddSub/MulDivと再帰下降で書いていたのを手直ししていただけなので
正規表現で分解された要素が引数として与えられて処理する方式となるのは初めてのスタイルだ。
async/await を使えば再帰下降のままでも処理出来そうだが、Pascal側にその機能がないので先にコードが書けない。
四苦八苦してとりあえずpascal側である程度動作するコードは書けた。
Lazarusで正規表現を使う例とかで参考になるかもしれないので現時点でのコードを公開してる=>
z変換で周波数特性を得るjavascript作成奮戦記
これからこの千行付近あるコードをJavaScriptに直すのはちょっと頑張らないといけない
タグ:正規表現
2023年03月05日
LazarusでprintfDebug
Lazarusにはデバッグコンソールがなく GUIアプリだとprintfデバッグが面倒です。
GUI画面が固まる前ならメモ帳でも張り付けて出力出来るのですけどね。
もちろんGUIからでも windows.AllocConsole;でコンソールは開くのですが
その場合 コンソールを手動で閉じてしまうとアプリも落ちてしまいます。
つい邪魔でデバッグ中にコンソールを閉じたつもりでアプリを終了させてデバッグ失敗をなくすために
コンソール窓の右上の[X]を無効にしてしまいます。
それは2か所からの公開ノウハウの組み合わせです。リンクを示しています。
リンク先に感謝を
var MyConsoleWhd: HWND = 0;
MyConsoleBuf:array [0..0] of AnsiChar;
procedure OpenConsole;
var
OldWindowTitle, NewWindowTitle: ansistring;
mHMENU: HMENU;
begin
AllocConsole(); //false=失敗するのは既に開いている時だけ
if MyConsoleWhd = 0 then //初めて呼ばれた時
begin
System.Assign(System.OutPut, 'CONOUT$');
System.ReWrite(System.OutPut);
System.SetTextBuf(System.OutPut,MyConsoleBuf); //バッファがある間は文字が出力されないので
// System.SetTextCodePage(System.OutPut ,GetOEMCP()) ; //これをしても効果ない
//https://learn.microsoft.com/ja-jp/troubleshoot/windows-server/performance/obtain-console-window-handle
NewWindowTitle := format('OpenConsole%15.10f', [now]);
OldWindowTitle := '';
SetLength(OldWindowTitle, 512);
GetConsoleTitleA(@OldWindowTitle[1], 512 - 1); //タイトル文字を保存して
SetConsoleTitleA(pansichar(NewWindowTitle)); //適当なタイトルを付けて
Sleep(40);
MyConsoleWhd := FindWindowA(nil, pansichar(NewWindowTitle)); // 窓ハンドル得る
SetConsoleTitle(pansichar(OldWindowTitle)); //タイトル文字を復元
//https://atmarkit.itmedia.co.jp/fdotnet/dotnettips/896conclosebtn/conclosebtn.html
// [閉じる]ボタンの無効化
if MyConsoleWhd <> 0 then
begin
mHMENU := GetSystemMenu(MyConsoleWhd, False);
RemoveMenu(mHMENU, SC_CLOSE, MF_BYCOMMAND);
end;
end;
end;
コンソールに出力する時、GUIから開いたコンソールではwriteは出来ません。
どうやら stdoutのファイル型 が初期化されていないようです。
上では'CONOUT$'でファイル変数を開くようにしています。(ただしコンソールアプリモードとは挙動が違うようです)
もともとLazarusはUTF8ですからそのままコンソールにwriteで出力すれば文字化けします。
そこで文字化けしない文字列用の出力コマンドの例を
procedure cPutS(s: ansistring);
var
rdSize: DWORD;
begin
OpenConsole; //コンソールのコードページも変更可能だが
// SetConsoleOutputCP(StringCodePage(s));//こうやってもUTF8は文字化けするので
SetCodePage(RawByteString(s),GetOEMCP() ,true); //文字列のコードページを変更する
//せっかく変更しても s+#13#10 と文字列の加算するだけでUTF8に戻るから2行に分ける
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), @s[1], length(s), rdSize, nil);
s := #13#10;
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), @s[1], length(s), rdSize, nil);
//rdSizeは見てない。まあ出力出来なかったらどうしろというのか判らないし
end;
のような関数を別に用意してやります
GUI画面が固まる前ならメモ帳でも張り付けて出力出来るのですけどね。
もちろんGUIからでも windows.AllocConsole;でコンソールは開くのですが
その場合 コンソールを手動で閉じてしまうとアプリも落ちてしまいます。
つい邪魔でデバッグ中にコンソールを閉じたつもりでアプリを終了させてデバッグ失敗をなくすために
コンソール窓の右上の[X]を無効にしてしまいます。
それは2か所からの公開ノウハウの組み合わせです。リンクを示しています。
リンク先に感謝を
var MyConsoleWhd: HWND = 0;
MyConsoleBuf:array [0..0] of AnsiChar;
procedure OpenConsole;
var
OldWindowTitle, NewWindowTitle: ansistring;
mHMENU: HMENU;
begin
AllocConsole(); //false=失敗するのは既に開いている時だけ
if MyConsoleWhd = 0 then //初めて呼ばれた時
begin
System.Assign(System.OutPut, 'CONOUT$');
System.ReWrite(System.OutPut);
System.SetTextBuf(System.OutPut,MyConsoleBuf); //バッファがある間は文字が出力されないので
// System.SetTextCodePage(System.OutPut ,GetOEMCP()) ; //これをしても効果ない
//https://learn.microsoft.com/ja-jp/troubleshoot/windows-server/performance/obtain-console-window-handle
NewWindowTitle := format('OpenConsole%15.10f', [now]);
OldWindowTitle := '';
SetLength(OldWindowTitle, 512);
GetConsoleTitleA(@OldWindowTitle[1], 512 - 1); //タイトル文字を保存して
SetConsoleTitleA(pansichar(NewWindowTitle)); //適当なタイトルを付けて
Sleep(40);
MyConsoleWhd := FindWindowA(nil, pansichar(NewWindowTitle)); // 窓ハンドル得る
SetConsoleTitle(pansichar(OldWindowTitle)); //タイトル文字を復元
//https://atmarkit.itmedia.co.jp/fdotnet/dotnettips/896conclosebtn/conclosebtn.html
// [閉じる]ボタンの無効化
if MyConsoleWhd <> 0 then
begin
mHMENU := GetSystemMenu(MyConsoleWhd, False);
RemoveMenu(mHMENU, SC_CLOSE, MF_BYCOMMAND);
end;
end;
end;
コンソールに出力する時、GUIから開いたコンソールではwriteは出来ません。
どうやら stdoutのファイル型 が初期化されていないようです。
上では'CONOUT$'でファイル変数を開くようにしています。(ただしコンソールアプリモードとは挙動が違うようです)
もともとLazarusはUTF8ですからそのままコンソールにwriteで出力すれば文字化けします。
そこで文字化けしない文字列用の出力コマンドの例を
procedure cPutS(s: ansistring);
var
rdSize: DWORD;
begin
OpenConsole; //コンソールのコードページも変更可能だが
// SetConsoleOutputCP(StringCodePage(s));//こうやってもUTF8は文字化けするので
SetCodePage(RawByteString(s),GetOEMCP() ,true); //文字列のコードページを変更する
//せっかく変更しても s+#13#10 と文字列の加算するだけでUTF8に戻るから2行に分ける
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), @s[1], length(s), rdSize, nil);
s := #13#10;
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), @s[1], length(s), rdSize, nil);
//rdSizeは見てない。まあ出力出来なかったらどうしろというのか判らないし
end;
のような関数を別に用意してやります
2023年03月03日
SIMD命令をDelphiでも動かす
Delphiといっても普段使ってるのはDelphi5です。XEあたりまでは飛び飛びのバージョンでも持っていますが、軽くて小さいexeが作れて使用許諾がオンラインでないので仮想環境でも面倒なく使えますからね。
AVX命令で10倍もの速度が出るのならDelphi5でも使ってみたくなります。
問題はDelphi5のアセンブラにはAVXどころかSSEもない事です。
そこでFPCのObjDump(GCCのと同趣旨のもの)を呼び出して アセンブラ命令を DB命令(バイトデータ)に置き換えて見事に使えてしまいました。
Lazarusで作ったSIMD関数をDelphiで使うのページです
このページには ObjDump.exeを呼び出して取り込むツールのソースが入っているので、
何かコンソールアプリ起動して出力を処理したい人は参考にして下さい。
AVX命令で10倍もの速度が出るのならDelphi5でも使ってみたくなります。
問題はDelphi5のアセンブラにはAVXどころかSSEもない事です。
そこでFPCのObjDump(GCCのと同趣旨のもの)を呼び出して アセンブラ命令を DB命令(バイトデータ)に置き換えて見事に使えてしまいました。
Lazarusで作ったSIMD関数をDelphiで使うのページです
このページには ObjDump.exeを呼び出して取り込むツールのソースが入っているので、
何かコンソールアプリ起動して出力を処理したい人は参考にして下さい。
2023年03月01日
32bitの方が64bitより高速で精度がよい
SIMDでD[i]:=D[i] + coff*S[i]を計算するのはだいたい終わって、
SIMDでΣai*biの計算を作ってる。
それで面白い事に気づいた。
SIMDを使わないLazarusの計算結果は32bitLazarusの方が早いのだ。しかもΣai*biに限れば Lazarusは単精度のままでも非常に精度がよい。
違いが出るのはWin64では浮動小数点にFPU(x87)を使わない事にある。
FPUを使うと変数は単精度でも内部での処理は常に80bitになる。
Win64ではSSE命令で処理するから単精度の積和は精度が悪い。
もっとも遅いのはLazarusのコンパイラがSSEを使っても並列化をしてくれない事にある。
結果、現在作成中のAVXのΣai*biの計算では1桁もの差が出る。
SIMDでΣai*biの計算を作ってる。
それで面白い事に気づいた。
SIMDを使わないLazarusの計算結果は32bitLazarusの方が早いのだ。しかもΣai*biに限れば Lazarusは単精度のままでも非常に精度がよい。
違いが出るのはWin64では浮動小数点にFPU(x87)を使わない事にある。
FPUを使うと変数は単精度でも内部での処理は常に80bitになる。
Win64ではSSE命令で処理するから単精度の積和は精度が悪い。
もっとも遅いのはLazarusのコンパイラがSSEを使っても並列化をしてくれない事にある。
結果、現在作成中のAVXのΣai*biの計算では1桁もの差が出る。
2023年02月27日
LazarusでRDTSCで処理時間を見てみた
SIMD命令で並列化コードを少し書いてみたが、実際にどれくらい速度が出るのか見てみたい。
そこでRDTSC命令を使ったライブラリを書いてみた。
単精度のFOR文でAVXの8並列とSSEの4並列を比較してみたが、AVXの方がSSEより遅い結果が出てしまった。
そこで計算する順番を入れ替えると倍以上も速度がAVXが早いという当然の結果が出た。
どうやら 256個のデータで比較したせいか 命令キャッシュに2回目の呼び出しが収まるせいで2回目の処理時間が短くなるようだ
そこでRDTSC命令を使ったライブラリを書いてみた。
単精度のFOR文でAVXの8並列とSSEの4並列を比較してみたが、AVXの方がSSEより遅い結果が出てしまった。
そこで計算する順番を入れ替えると倍以上も速度がAVXが早いという当然の結果が出た。
どうやら 256個のデータで比較したせいか 命令キャッシュに2回目の呼び出しが収まるせいで2回目の処理時間が短くなるようだ
2023年02月26日
アセンブラを学ぶならLazarusが便利
ネイティブコンパイラで プログラムのデバッグをするとき、アセンブラが読めると便利です。
それが出来ないと、いわゆるPrintfデバッグ(Pascalだとwriteとか formatしてメモ帳に出力するとか)となります
アセンブラが読めるならブレークポイントを仕掛けてコンパイルがどうされたか見たり、レジスタの値を見たりと情報の幅が広がります。
アセンブラを学ぶのならLazarusは良い環境です。
asm文でアセンブラを埋め込め、そのアセンブラ文法もLinuxスタイルですが
Intelスタイルも{$ASMMODE intel} をasmの前に入れるだけで書けます。
アセンブラの文法は殆ど同じになりますが、コメントはPascal式 //より右か {この範囲} か (* この範囲 *)
アセンブラだけで何かプログラムするとなればPICとか1チップマイコンくらいになりますから、高級言語の中に埋め込むという方式で入るのがお勧めの理由です。
私もLazarusでWin64環境を始めたので四苦八苦です=>Lazarusでアセンブラを使う時の注意点
asm文を使わなければ出来ない事は色々あります。
・前回のようにCPUIDを読んで自分のPCのCPUの種類や機能を調べる
・パスカル言語にSAR 演算子が無い事の対策
・64bit以上の多倍長演算(アセンブラならフラグを使えるので)
・X*Y/X のように途中で桁溢れする計算の計算可能範囲を広げる
・コンパイラが使ってくれないSIMD命令の利用
もっとも、最近はスクリプト言語が隆盛なので意味がないのかもしれませんけどね。
それが出来ないと、いわゆるPrintfデバッグ(Pascalだとwriteとか formatしてメモ帳に出力するとか)となります
アセンブラが読めるならブレークポイントを仕掛けてコンパイルがどうされたか見たり、レジスタの値を見たりと情報の幅が広がります。
アセンブラを学ぶのならLazarusは良い環境です。
asm文でアセンブラを埋め込め、そのアセンブラ文法もLinuxスタイルですが
Intelスタイルも{$ASMMODE intel} をasmの前に入れるだけで書けます。
アセンブラの文法は殆ど同じになりますが、コメントはPascal式 //より右か {この範囲} か (* この範囲 *)
アセンブラだけで何かプログラムするとなればPICとか1チップマイコンくらいになりますから、高級言語の中に埋め込むという方式で入るのがお勧めの理由です。
私もLazarusでWin64環境を始めたので四苦八苦です=>Lazarusでアセンブラを使う時の注意点
asm文を使わなければ出来ない事は色々あります。
・前回のようにCPUIDを読んで自分のPCのCPUの種類や機能を調べる
・パスカル言語にSAR 演算子が無い事の対策
・64bit以上の多倍長演算(アセンブラならフラグを使えるので)
・X*Y/X のように途中で桁溢れする計算の計算可能範囲を広げる
・コンパイラが使ってくれないSIMD命令の利用
もっとも、最近はスクリプト言語が隆盛なので意味がないのかもしれませんけどね。
2023年02月25日
UTF8 ShiftJis EUC は先頭バイトだけで文字数が判ると知った
delphiを使ってからLazarusに移行する問題の1つが文字コードです。
(delphiはUTF16文字になってから移行しないままなので)
自作のツール類はShiftJISを想定しています。それらを移植しなけばいけません。
ちょっとしたコントロールも数値入力とかで四則演算出来るように作ったりしてる為です。
それで興味のなかった文字コードを調べていたわけですが
日本語で使われる UTF8 ShiftJis EUC は文字を順に読んで、そのも文字コードから1文字のバイト数が判るようになっていると知りました。
ShiftJis は当然最大2文字なので簡単 EUCは3バイトになる先行コードは1つだけ
そして UTF8も最初のバイトを見れば残り何バイトが必要かが判ります。
文字列処理といっても多くの場合、多バイト文字の中身は関係なく、多バイト文字である事が判れば十分です。
したがって、この3つは統一して処理出来そうです。
というような内容を文字列変換のページに追記しました
(delphiはUTF16文字になってから移行しないままなので)
自作のツール類はShiftJISを想定しています。それらを移植しなけばいけません。
ちょっとしたコントロールも数値入力とかで四則演算出来るように作ったりしてる為です。
それで興味のなかった文字コードを調べていたわけですが
日本語で使われる UTF8 ShiftJis EUC は文字を順に読んで、そのも文字コードから1文字のバイト数が判るようになっていると知りました。
ShiftJis は当然最大2文字なので簡単 EUCは3バイトになる先行コードは1つだけ
そして UTF8も最初のバイトを見れば残り何バイトが必要かが判ります。
文字列処理といっても多くの場合、多バイト文字の中身は関係なく、多バイト文字である事が判れば十分です。
したがって、この3つは統一して処理出来そうです。
ShiftJisは
OEMLeadBytes = [#$81..#$9F, #$E0..#$FC];
if c in OEMLeadBytes then 2byte else 1byte;
EUC-jpは
EUCLeadBytes = [#$8E,#$8F,#$A1..#$FE];
EUCLeadBytes3 = [#$8F]; //先頭文字が8Fなら合計3byte
if c in EUCLeadBytes then begin
if c in EUCLeadBytes3 then 3byte else 2byte;
end else 1byte
UTF8は
UTF8LeadBytes = [#$C2..#$DF, #$E0..#$EF, #$F0..#$F4];
UTF8LeadBytes2 = [#$C2..#$DF];
UTF8LeadBytes3 = [#$E0..#$EF];
UTF8LeadBytes4 = [#$F0..#$F4];
if c in UTF8LeadBytes then begin
if c in UTF8LeadBytes2 then 2byte
else
if c in UTF8LeadBytes3 then 3byte
else
if c in UTF8LeadBytes3 then 4byte;
end;
というような内容を文字列変換のページに追記しました
2023年02月24日
Lazarusで CPUIDで自分のPCのSSE対応状況を見る
SIMD命令を使ってみたくて、それには自分のPCでどのSSE/SVX命令に対応してるかどうか知る必要があります。
そこで書いてみました。結果、自分のPCは中古なのでSVX2まで対応でした。
それでも単精度の浮動小数点なら1度に8つの積+和が出来てしまいます。→ 実際にSIMD命令を使った関数
以下の使い方は GetSSE を呼び出すと列挙型が帰るので 使いたい命令があるかどうかは
if 使いたい命令 in GetSSE() then 〜
表示するなら対応する SSEname に文字列を用意しています。
昔にdelphiで作ったものを引っ張りだして修正したので sseに固執した名前付けだったり Lazarusなのにintelアセンブラ書式なのはご容赦下さい。
以下がGetSSE
そこで書いてみました。結果、自分のPCは中古なのでSVX2まで対応でした。
それでも単精度の浮動小数点なら1度に8つの積+和が出来てしまいます。→ 実際にSIMD命令を使った関数
以下の使い方は GetSSE を呼び出すと列挙型が帰るので 使いたい命令があるかどうかは
if 使いたい命令 in GetSSE() then 〜
表示するなら対応する SSEname に文字列を用意しています。
var i: TSSEMODE; s:string;
begin
s:=''; for i := low(SSEname) to High(SSEname) do
if i in d then s := s + ' ' + SSEname[i];
昔にdelphiで作ったものを引っ張りだして修正したので sseに固執した名前付けだったり Lazarusなのにintelアセンブラ書式なのはご容赦下さい。
以下がGetSSE
{$ASMMODE intel}
type
TSSEMODE = (sseSSE2, sseSSE3, sseFMA, sseSSE41, sseSSE42, sseAVX, sseAVX2, sseAVX512F, sseAVX512DQ);
TSSEMODEset = set of TSSEMODE;
const SSEname: array [low(TSSEMODE)..High(TSSEMODE)] of string =
('SSE2', 'SSE3', 'FMA', 'SSE41', 'SSE42', 'AVX', 'AVX2', 'AVX512F', 'AVX512DQ');
function GetSSE(): TSSEMODEset;
var iSZ, iDX, iCX, iBX: DWORD;
begin
asm
MOV EAX,0
CPUID
MOV iSZ,EAX
MOV EAX,1
CPUID
MOV iDX,EDX
MOV iCX,ECX
MOV EAX,7
MOV ECX,0
CPUID
MOV iBX,EBX
end ['EAX', 'ECX', 'EDX', 'EBX'];
if iSZ < 7 then iBX := 0;
Result := [];
if (iDX and (1 shl 26)) <> 0 then Include(Result, sseSSE2);
if (iCX and (1 shl 0)) <> 0 then Include(Result, sseSSE3);
if (iCX and (1 shl 12)) <> 0 then Include(Result, sseFMA);
if (iCX and (1 shl 19)) <> 0 then Include(Result, sseSSE41);
if (iCX and (1 shl 20)) <> 0 then Include(Result, sseSSE42);
if (iCX and (1 shl 28)) <> 0 then Include(Result, sseAVX);
if (iBX and (1 shl 5)) <> 0 then Include(Result, sseAVX2);
if (iBX and (1 shl 16)) <> 0 then Include(Result, sseAVX512F);
if (iBX and (1 shl 17)) <> 0 then Include(Result, sseAVX512DQ);
end;
2023年02月23日
Lazarusの文字化け対策
Lazarusの文字列はUTF8です。
そこにShiftJisを使おうとすると、最初の内はどうやっても勝手にUTF8になって文字化けして大変でした
windowsのコンソールアプリでwrite('漢字');でさえUTF8だから文字化けするのです
そしてShiftJisにいったん変換しても何かの呪いのように代入したり文字列連結すると文字化けする。
その仕組みがやっと分かりました。
LazarusのAnsiStringはコードページを変数内に持っていたのです。
そして文字定数はUTF8ですから文字列加算(連結)すればUTF8に変換されてしまう訳です。
そのあたりの細かい話をまとめてみました。
Lazarusの文字列変換
そこにShiftJisを使おうとすると、最初の内はどうやっても勝手にUTF8になって文字化けして大変でした
windowsのコンソールアプリでwrite('漢字');でさえUTF8だから文字化けするのです
そしてShiftJisにいったん変換しても何かの呪いのように代入したり文字列連結すると文字化けする。
その仕組みがやっと分かりました。
LazarusのAnsiStringはコードページを変数内に持っていたのです。
そして文字定数はUTF8ですから文字列加算(連結)すればUTF8に変換されてしまう訳です。
そのあたりの細かい話をまとめてみました。
Lazarusの文字列変換
タグ:文字列