mieki256's diary



2024/02/16(金) [n年前の日記]

#1 [basic] FreeBASICでsplit()を使いたい。その3

_昨日、 FreeBASIC上で split() 相当の処理を書いて実験していたけど、今日も少し実験。環境は Windows10 x64 22H2 + FreeBASIC 1.10.1 32bit。

フツーの言語なら組み込みで持ってるメソッドをわざわざ実装するとか、これは車輪の再発明に近いのではないか、無駄なことをしているのではないかと思ったりもするのだけど、そこからの流れで FreeBASIC の WString の不便さや文字列検索アルゴリズムのアレコレを知れたので、一応少しは勉強になったからまあいいか、と…。

空文字を除外するように修正 :

FreeBASICの公式Wikiで紹介されていた実装事例は、区切り文字が連続していた際に、空文字まで配列に登録されてしまっていた。

_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 で置換すれば済むわけでもなく。
  • 可変長文字列は使えない。固定長文字列(事前に確保した領域サイズから変更できない)になる。
  • サブルーチンや関数に配列変数を渡せない。

とりあえず、以下の方針で書いてみた。
  • 事前に確保する領域は、処理をするにあたって十分足りるであろう大きな領域にしておく。今回の場合、配列で、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の場合はどうすればいいのか…。

以上、1 日分です。

過去ログ表示

Prev - 2024/02 - Next
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project