2024/02/16(金) [n年前の日記]
#1 [basic] FreeBASICでsplit()を使いたい。その3
_昨日、
FreeBASIC上で split() 相当の処理を書いて実験していたけど、今日も少し実験。環境は Windows10 x64 22H2 + FreeBASIC 1.10.1 32bit。
フツーの言語なら組み込みで持ってるメソッドをわざわざ実装するとか、これは車輪の再発明に近いのではないか、無駄なことをしているのではないかと思ったりもするのだけど、そこからの流れで FreeBASIC の WString の不便さや文字列検索アルゴリズムのアレコレを知れたので、一応少しは勉強になったからまあいいか、と…。
フツーの言語なら組み込みで持ってるメソッドをわざわざ実装するとか、これは車輪の再発明に近いのではないか、無駄なことをしているのではないかと思ったりもするのだけど、そこからの流れで FreeBASIC の WString の不便さや文字列検索アルゴリズムのアレコレを知れたので、一応少しは勉強になったからまあいいか、と…。
◎ 空文字を除外するように修正 :
FreeBASICの公式Wikiで紹介されていた実装事例は、区切り文字が連続していた際に、空文字まで配列に登録されてしまっていた。
_Passing Arrays to Procedures - FreeBASIC Wiki Manual | FBWiki
_配列を手続きに渡す - ProPgPassingArrays
そのあたりをちょっとだけ修正してみた。
_splitstringb2.bi
以下、使用サンプル。fbc test_splitstringb2.bas でコンパイル。
_test_splitstringb2.bas
test_splitstringb2.exe の実行結果。
空文字を除外できている。
ちなみに、この処理の中で使ってる instr() は処理が遅いから、もし高速化を試みるなら使わないようにするべき、という主張も見かけたのだけど。どんな処理をしているのかソースが分かりやすくなるし、マルチバイト文字列への対応も楽になりそうだから、使ってしまってもいいのではないかと思ったりもした。膨大な長さの文字列を大量に処理しなければいけない場面が出てきたら、その時にまた考えよう…。
_Passing Arrays to Procedures - FreeBASIC Wiki Manual | FBWiki
_配列を手続きに渡す - ProPgPassingArrays
そのあたりをちょっとだけ修正してみた。
_splitstringb2.bi
#ifndef __SPLITSTRINGB2__ #define __SPLITSTRINGB2__ Sub splitStringB2(ByVal src As String, ByVal delim As String, result(Any) As String) Do Dim As Integer i = InStr(1, src, Chr(delim[0])) ' search delimiter If i = 0 Then ' not found delimiter. exit loop ReDim Preserve result(UBound(result) + 1) result(UBound(result)) = src Exit Do End If Dim As String word = Left(src, i - 1) If word <> "" Then ReDim Preserve result(UBound(result) + 1) result(UBound(result)) = word ' save word to array End If src = Mid(src, i + 1) ' delete word Loop End Sub #endif
以下、使用サンプル。fbc test_splitstringb2.bas でコンパイル。
_test_splitstringb2.bas
#include "splitstringb2.bi" Dim As String text text = "char id=32 x=250 y=22 width=5 height=5 xoffset=-2 yoffset=21 xadvance=7 page=0 chnl=15" 'text = "info face=""DejaVu Sans"" size=24 bold=1 italic=0 charset=""ANSI"" unicode=0 stretchH=100 smooth=1 aa=4 padding=0,0,0,0 spacing=1,1 outline=2" Dim words() As String splitStringB2(text, " ", words()) Print "[" & text & "]" For i As Integer = 0 To UBound(words) Print i & ": [" & words(i) & "]" Next i Print "end."
test_splitstringb2.exe の実行結果。
> test_splitstringb2.exe [char id=32 x=250 y=22 width=5 height=5 xoffset=-2 yoffset=21 xadvance=7 page=0 chnl=15] 0: [char] 1: [id=32] 2: [x=250] 3: [y=22] 4: [width=5] 5: [height=5] 6: [xoffset=-2] 7: [yoffset=21] 8: [xadvance=7] 9: [page=0] 10: [chnl=15] end.
空文字を除外できている。
ちなみに、この処理の中で使ってる instr() は処理が遅いから、もし高速化を試みるなら使わないようにするべき、という主張も見かけたのだけど。どんな処理をしているのかソースが分かりやすくなるし、マルチバイト文字列への対応も楽になりそうだから、使ってしまってもいいのではないかと思ったりもした。膨大な長さの文字列を大量に処理しなければいけない場面が出てきたら、その時にまた考えよう…。
◎ WStringで同じ処理をしてみる :
前述の処理は、1文字=1byteとして扱う String を使った時の事例だけど。マルチバイト文字列を扱える WString を使って同じことをしてみたい。
ただ、FreeBASIC の WString は以下の制限があるので、String を WString で置換すれば済むわけでもなく。
とりあえず、以下の方針で書いてみた。
.basファイルは SJIS で書いてみた。
_test_splitw.bas
fbc test_splitw.bas でコンパイル。実行結果は以下。
区切り文字は半角空白。分割できている。区切り文字が連続しているところも除外できている。
ただ、これはマクロなので…。サブルーチン化できたらいいのだけどな…。
ただ、FreeBASIC の WString は以下の制限があるので、String を WString で置換すれば済むわけでもなく。
- 可変長文字列は使えない。固定長文字列(事前に確保した領域サイズから変更できない)になる。
- サブルーチンや関数に配列変数を渡せない。
とりあえず、以下の方針で書いてみた。
- 事前に確保する領域は、処理をするにあたって十分足りるであろう大きな領域にしておく。今回の場合、配列で、256文字 x 256個あれば大丈夫かな…と…。
- サブルーチンに入れずに マクロ機能 (#macro - #endmacro) で代替してみる。
.basファイルは SJIS で書いてみた。
_test_splitw.bas
Dim As WString * 1024 text, delim text = "新しい 朝が来た 希望の 朝だ" delim = " " Dim result(256) As WString * 256 ' max 256 length x 256 ' ---------------------------------------- #macro splitW( s, d, a ) Dim As WString * 1024 src = s Dim As Integer n = 0 Do Dim As Integer i = InStr(1, src, WChr(d[0])) ' search delimiter If i = 0 Then ' not found delimiter. exit Loop If src <> "" Then a(n) = src n += 1 End If Exit Do End If Dim As WString * 256 word word = Left(src, i - 1) If word <> "" Then a(n) = word ' save word to Array n += 1 End If src = Mid(src, i + 1) ' delete word Loop #endmacro ' ---------------------------------------- splitW(text, delim, result) Print "[" & text & "]" For i As Integer = 0 To UBound(result) If result(i) = "" Then Exit For Print i & ": [" & result(i) & "]" Next i Print "end."
fbc test_splitw.bas でコンパイル。実行結果は以下。
> test_splitw.exe [新しい 朝が来た 希望の 朝だ] 0: [新しい] 1: [朝が来た] 2: [希望の] 3: [朝だ] end.
区切り文字は半角空白。分割できている。区切り文字が連続しているところも除外できている。
ただ、これはマクロなので…。サブルーチン化できたらいいのだけどな…。
◎ ユーザ定義型を使うのも手だろうか :
FreeBASIC にはユーザ定義型という機能もある。C言語で言うところの構造体だろうか。
そのユーザ定義型の中に、WString を格納してしまえば、String のような感覚で使える状態にできなくもないらしい。
おそらくはそういう方法で用意された、DWString という型が紹介されていた。
_DWSTRING.bi - Dynamic null terminated unicode string data type - freebasic.net
これを使えば、マルチバイト文字列に対して処理する際も、String のような感覚で書ける可能性がありそう。たぶん。ただ、これは Windows限定のライブラリに見える…。Linuxの場合はどうすればいいのか…。
そのユーザ定義型の中に、WString を格納してしまえば、String のような感覚で使える状態にできなくもないらしい。
おそらくはそういう方法で用意された、DWString という型が紹介されていた。
_DWSTRING.bi - Dynamic null terminated unicode string data type - freebasic.net
これを使えば、マルチバイト文字列に対して処理する際も、String のような感覚で書ける可能性がありそう。たぶん。ただ、これは Windows限定のライブラリに見える…。Linuxの場合はどうすればいいのか…。
[ ツッコむ ]
以上です。