source: trunk/Include/Classes/ActiveBasic/Strings/SPrintF.ab @ 468

Last change on this file since 468 was 468, checked in by イグトランス (egtra), 15 years ago

TextWriter?, StreamWriterの追加。
SPrintfの浮動小数点数変換で、NaN, Infiniteの出力に対応。
PathとDirectoryInfoのCreateDirectoryで、対象が既に存在するときには例外を投げないように修正。
SimpleTestCase内で使用する一時フォルダの場所にGetTempPathで取得する版を追加(コメントアウト)。

File size: 48.0 KB
Line 
1/*!
2@file Include/Classes/ActiveBasic/Strings/SPrintF.ab
3@brief SPrintfとその補助ルーチンが含まれるファイル。
4
5SPrintfで数値変換が行われるとき、呼出の階層は、
6SPrintf→FormatInteger, FormatFloat→FormatIntegerEx, FormatFloatExとなる。
7
8また、次のような変換ルーチンが存在する。
9@li FormatFloatE
10@li FormatFloatF
11@li FormatFloatG
12@li FormatFloatA
13@li FormatIntegerD
14@li FormatIntegerU
15@li FormatIntegerO
16@li FormatIntegerX
17@li FormatCharacter
18@li FormatString
19*/
20
21Namespace ActiveBasic
22Namespace Strings
23
24Namespace Detail
25
26/*!
27@brief  浮動小数点数を文字列化する低水準な関数。符号、指数、仮数に分けて出力。
28@author Egtra
29@date   2007/09/18
30@param[in]  x   文字列化する浮動小数点数
31@param[out] e   指数
32@param[out] sign    符号
33@return 仮数
34仮数は1の位から下へ17桁で、小数点を含まない。そのため、誤差を無視すればVal(仮数) * 10 ^ (e - 17) = Abs(x)が成り立つ。
35
36xに無限大、非数を渡した場合の動作は未定義。
37*/
38Function FloatToChars(x As Double, ByRef e As Long, ByRef sign As Boolean) As String
39    Imports System
40
41    '0を弾く
42    If x = 0 Then
43        If GetQWord(VarPtr(x) As *QWord) And &h8000000000000000 Then
44            sign = True
45        Else
46            sign = False
47        End If
48
49        e = 0
50        FloatToChars = "00000000000000000"
51        Exit Function
52    End If
53
54    '符号の判断(同時に符号を取り除く)
55    If x < 0 Then
56        sign = True
57        x = -x
58    Else
59        sign = False
60    End If
61
62    '1e16 <= x < 1e17へ正規化
63    '(元のx) = (正規化後のx) ^ (d - 17)である。
64    Dim d = Math.Floor(Math.Log10(x)) As Long
65    If d < 16 Then
66        x *= ipow(10, +17 - d)
67    ElseIf d > 16 Then
68        x /= ipow(10, -17 + d)
69    End If
70
71    '補正
72    While x < 1e16
73        x *= 10
74        d--
75    Wend
76    While x >= 1e17
77        x /= 10
78        d++
79    Wend
80
81    d--
82    e = d
83
84    FloatToChars = FormatIntegerLU((x As Int64) As QWord, 17, 0, None)
85End Function
86
87/*!
88@brief  書式化関数群で使用するフラグ。
89@author Egtra
90@date   2007/09/18
91*/
92Const Enum FormatFlags
93    '! 何も指定がない。
94    None = &h0
95    /*!
96    符号、+。符号付変換[diAaEeFfGg]のとき、正の値でも符号を付ける。
97    AdjustFieldWidthの仕様から、Format関数郡内からAdjustFieldWidthにかけて、
98    単に数値が符号付である(負の値である)ことを示す意味でも用いられる。
99    */
100    Sign = &h1
101    /*! 空白、空白文字。
102    符号付変換[diAaEeFfGg]のとき、正の値ならば符号分の空白を開ける。Signが立っているときには無視される。
103    */
104    Blank = &h2
105    /*! ゼロ、0。
106    [diouXxAaEeFfGg]で、フィールドの空きを0で埋める。leftが立っているときには無視される。
107    */
108    Zero = &h4
109    '! 左揃え、-。フィールド内で左揃えにする。
110    LeftSide = &h8
111    /*! 代替表記、#。
112    @li [OoXx]では、値が0でない場合、先頭に0、0xを付ける。</ul>
113    @li [AaEeFfGg]では、精度0でも小数点を付ける。</ul>
114    @li [Gg]では、それに加え、小数部末尾の0の省略を行わないようにする。</ul>
115    */
116    Alt = &h10
117    '! 大文字。使用するアルファベットを大文字にする。[aefgx]を[AEFGX]化する。
118    Cap = &h20
119
120    '!BASIC接頭辞。&h, &oなど。
121    BPrefix = &h40
122
123    /*!
124    内部処理用に予約。
125    @note   Minusとして使用されている。
126    */
127    Reserved = &h80000000
128End Enum
129
130/*!
131@brief  浮動小数点数をprintfの%e, %E(指数形式、十進法)相当の変換で文字列化する関数。
132@author Egtra
133@date   2007/09/18
134@param[in] x    文字列化する浮動小数点数値。
135@param[in] precision    精度。小数点以下の桁数。DWORD_MAXのとき、指定なしとして既定値6となる。
136@param[in] field    フィールド幅。
137@param[in] flags    書式フラグ。
138@return xの文字列表現
139*/
140Function FormatFloatE(x As Double, precision As DWord, field As DWord, flags As FormatFlags) As String
141    FormatFloatE = FormatFloatEx(AddressOf(FormatFloatE_Convert), x, precision, field, flags)
142End Function
143
144/*
145@brief  浮動小数点数をprintfの%e, %E(指数形式、十進法)相当の変換で文字列化する関数。
146@author Egtra
147@date   2008/03/08
148@param[in,out] sb 書式化した文字列を追加するバッファ。
149@param[in] x    文字列化する浮動小数点数値。
150@param[in] precision    精度。小数点以下の桁数。DWORD_MAXのとき、指定なしとして既定値6となる。
151@param[in] field    フィールド幅。
152@param[in] flags    書式フラグ。
153*/
154Sub FormatFloatE(sb As System.Text.StringBuilder, x As Double, precision As DWord, field As DWord, ByRef flags As FormatFlags)
155    FormatFloatEx(sb, AddressOf(FormatFloatE_Convert), x, precision, field, flags)
156End Sub
157
158/*!
159@brief  浮動小数点数をprintfの%e, %E(指数形式、十進法)相当の変換で文字列化する関数。
160@author Egtra
161@date   2008/03/07
162@param[in,out] sb 書式化した文字列を追加するバッファ。
163@param[in] x    文字列化する浮動小数点数値。
164@param[in] precision    精度。小数点以下の桁数。DWORD_MAXのとき、指定なしとして15となる。
165@param[in] field    フィールド幅。
166@param[in,out] flags    書式フラグ。
167@todo   他の実装での末尾桁の扱いを調べる(このコードでは何もしていないので切捨となっている)。
168*/
169Sub FormatFloatE_Convert(sb As System.Text.StringBuilder, x As Double, precision As DWord, field As DWord, ByRef flags As FormatFlags)
170    If precision = DWORD_MAX Then
171        precision = 15
172    End If
173    Dim e As Long, negative As Boolean
174    Dim s = FloatToChars(x, e, negative)
175    FormatFloatE_Base(sb, s, negative, precision, flags)
176    FormatFloatE_Exponent(sb, e, flags)
177End Sub
178
179/**
180@brief  FormatFloatEの符号・基数部の出力用。
181@author Egtra
182@date   2007/10/27
183*/
184Sub FormatFloatE_Base(sb As System.Text.StringBuilder, s As String, negative As Boolean, precision As DWord, ByRef flags As FormatFlags)
185    With sb
186        AppendSign(sb, negative, flags)
187        .Append(s[0])
188        If (flags And Alt) Or precision > 0 Then
189            .Append(".")
190            Dim outputLen = s.Length - 1
191            If outputLen >= precision Then
192                .Append(s, 1, precision)
193            Else 'sで用意された桁が指定された精度より少ないとき
194                .Append(s, 1, outputLen)
195                .Append(&h30 As Char, precision - outputLen) '足りない桁は0埋め
196            End If
197        End If
198    End With
199End Sub
200
201/**
202@brief  FormatFloatEの指数部の出力用。
203@author Egtra
204@date   2007/10/27
205*/
206Sub FormatFloatE_Exponent(sb As System.Text.StringBuilder, e As Long, flags As FormatFlags)
207    With sb
208        If flags And Cap Then
209            .Append("E")
210        Else
211            .Append("e")
212        End If
213        FormatIntegerD(sb, e, 2, 0, Sign Or Zero)
214    End With
215End Sub
216
217/*!
218@brief  浮動小数点数をprintfの%f(小数形式、十進法)相当の変換で文字列化する関数。
219@author Egtra
220@date   2007/10/23
221@param[in]  x   文字列化する浮動小数点数値。
222@param[in]  precision   精度。小数点以下の桁数。DWORD_MAXのとき、指定なしとして既定値15となる。
223@param[in]  field   フィールド幅。
224@param[in]  flags   書式フラグ。
225@return xの文字列表現
226*/
227Function FormatFloatF(x As Double, precision As DWord, field As DWord, flags As FormatFlags) As String
228    FormatFloatF = FormatFloatEx(AddressOf(FormatFloatF_Convert), x, precision, field, flags)
229End Function
230
231/*
232@brief  浮動小数点数をprintfの%f(小数形式、十進法)相当の変換で文字列化する関数。
233@author Egtra
234@date   2008/03/08
235@param[in,out] sb 書式化した文字列を追加するバッファ。
236@param[in] x    文字列化する浮動小数点数値。
237@param[in] precision    精度。小数点以下の桁数。DWORD_MAXのとき、指定なしとして既定値6となる。
238@param[in] field    フィールド幅。
239@param[in] flags    書式フラグ。
240*/
241Sub FormatFloatF(sb As System.Text.StringBuilder, x As Double, precision As DWord, field As DWord, ByRef flags As FormatFlags)
242    FormatFloatEx(sb, AddressOf(FormatFloatF_Convert), x, precision, field, flags)
243End Sub
244
245/*!
246@brief  浮動小数点数をprintfの%f(小数形式、十進法)相当の変換で文字列化する関数。
247@author Egtra
248@date   2008/03/07
249@param[in,out] sb 書式化した文字列を追加するバッファ。
250@param[in]  x   文字列化する浮動小数点数値。
251@param[in]  precision   精度。小数点以下の桁数。DWORD_MAXのとき、指定なしとして既定値15となる。
252@param[in]  field   フィールド幅。
253@param[in,out]  flags   書式フラグ。
254*/
255Sub FormatFloatF_Convert(sb As System.Text.StringBuilder, x As Double, precision As DWord, field As DWord, ByRef flags As FormatFlags)
256    If precision = DWORD_MAX Then
257        precision = 15
258    End If
259    Dim e As Long, negative As Boolean
260    Dim s = FloatToChars(x, e, negative)
261    FormatFloatF_Core(sb, s, e, negative, precision, flags)
262End Sub
263
264/**
265@author Egtra
266@date   2007/10/27
267*/
268Sub FormatFloatF_Core(sb As System.Text.StringBuilder, s As String, e As Long, negative As Boolean, precision As DWord, ByRef flags As FormatFlags)
269    With sb
270        AppendSign(sb, negative, flags)
271
272        Dim intPartLen = e + 1
273        Dim outputDigit = 0 As DWord
274        If intPartLen >= 17 Then
275            '有効桁が全て整数部に収まる場合
276            .Append(s)
277            .Append(&h30 As Char, intPartLen - 17)
278            outputDigit = 17
279        ElseIf intPartLen > 0 Then
280            '有効桁の一部が整数部にかかる場合
281            .Append(s, 0, intPartLen)
282            outputDigit = intPartLen
283        Else
284            '有効桁が全く整数部にかからない場合
285            .Append(&h30 As Char)
286        End If
287
288        If precision > 0 Or (flags And Alt) Then
289            .Append(".")
290
291            Dim lastDigit = s.Length - outputDigit
292            If lastDigit >= precision Then '変換して得られた文字列の桁数が精度以上ある場合
293                Dim zeroDigit = 0
294                If intPartLen < 0 Then
295                    '1.23e-4 = 0.000123のように指数が負のため小数点以下に0が続く場合
296                    zeroDigit = System.Math.Min(-intPartLen As DWord, precision)
297                    .Append(&h30 As Char, zeroDigit As Long)
298                End If
299                .Append(s, outputDigit, (precision - zeroDigit) As Long)
300            Else
301                .Append(s, outputDigit, lastDigit)
302                .Append(&h30 As Char, (precision - lastDigit) As Long) '残りの桁は0埋め
303            End If
304        End If
305    End With
306End Sub
307
308/*!
309@brief  浮動小数点数をprintfの%g, %G(小数・指数、十進法)相当の変換で文字列化する関数。
310@author Egtra
311@date   2007/10/23
312@param[in]  x   文字列化する浮動小数点数値。
313@param[in]  precision   精度。小数点以下の桁数。DWORD_MAXまたは0のとき、指定なしとして既定値15となる。
314@param[in]  field   フィールド幅。
315@param[in]  flags   書式フラグ。
316@return xの文字列表現
317@todo   下位桁の扱いの調査。
318*/
319Function FormatFloatG(x As Double, precision As DWord, field As DWord, flags As FormatFlags) As String
320    FormatFloatG = FormatFloatEx(AddressOf(FormatFloatG_Convert), x, precision, field, flags)
321End Function
322
323/*
324@brief  浮動小数点数をprintfの%g, %G(小数・指数、十進法)相当の変換で文字列化する関数。
325@author Egtra
326@date   2008/03/08
327@param[in,out] sb 書式化した文字列を追加するバッファ。
328@param[in] x    文字列化する浮動小数点数値。
329@param[in] precision    精度。小数点以下の桁数。DWORD_MAXのとき、指定なしとして既定値6となる。
330@param[in] field    フィールド幅。
331@param[in] flags    書式フラグ。
332*/
333Sub FormatFloatG(sb As System.Text.StringBuilder, x As Double, precision As DWord, field As DWord, ByRef flags As FormatFlags)
334    FormatFloatEx(sb, AddressOf(FormatFloatG_Convert), x, precision, field, flags)
335End Sub
336
337/*!
338@brief  浮動小数点数をprintfの%g, %G(小数・指数、十進法)相当の変換で文字列化する関数。
339@author Egtra
340@date   2007/10/23
341@param[in,out] sb 書式化した文字列を追加するバッファ。
342@param[in]  x   文字列化する浮動小数点数値。
343@param[in]  precision   精度。小数点以下の桁数。DWORD_MAXまたは0のとき、指定なしとして既定値15となる。
344@param[in]  field   フィールド幅。
345@param[in,out]  flags   書式フラグ。
346@todo   下位桁の扱いの調査。
347*/
348Sub FormatFloatG_Convert(sb As System.Text.StringBuilder, x As Double, precision As DWord, field As DWord, ByRef flags As FormatFlags)
349    'GではE/Fと違い整数部も有効桁数に数えるのでその分を引いておく。
350    If precision = DWORD_MAX Or precision = 0 Then
351        precision = 14
352    Else
353        precision--
354    End If
355    Dim lastLength = sb.Length
356    Dim e As Long, negative As Boolean
357    Dim s = FloatToChars(x, e, negative)
358    If -5 < e And e < precision Then
359        FormatFloatF_Core(sb, s, e, negative, -e + precision, flags)
360        FormatFloatG_RemoveLowDigit(sb, lastLength, flags)
361    Else
362        FormatFloatE_Base(sb, s, negative, precision, flags)
363        FormatFloatG_RemoveLowDigit(sb, lastLength, flags)
364        FormatFloatE_Exponent(sb, e, flags)
365    End If
366End Sub
367
368/*!
369@brief  FormatFloatG/A用の小数点以下末尾の0を削除するルーチン
370@author Egtra
371@date   2007/10/27
372@param[in, out] sb 文字列バッファ
373@param[in]  flags フラグ
374flagsでAltが立っているとき、この関数は何もしない。
375*/
376Sub FormatFloatG_RemoveLowDigit(sb As System.Text.StringBuilder, start As Long, flags As FormatFlags)
377    Imports ActiveBasic.Strings
378   
379    Dim count = sb.Length
380    If (flags And Alt) = 0 Then
381        Dim p = StrPtr(sb)
382        Dim point = ChrFind(VarPtr(p[start]), (count - start) As SIZE_T, Asc("."))
383        If point = -1 Then
384            Debug
385        End If
386
387        Dim i As Long
388        For i = count - 1 To point + 1 Step -1
389            If sb[i] <> &h30 Then
390                Exit For
391            End If
392        Next
393        If i <> point Then
394            i++
395        End If
396        sb.Length = i
397    End If
398End Sub
399
400/*!
401@brief  浮動小数点数をprintfの%a, %A(指数形式、十六進法)相当の変換で文字列化する関数。
402@author Egtra
403@date   2007/09/18
404@param[in] x    文字列化する浮動小数点数値。
405@param[in] precision    精度。小数点以下の桁数。DWORD_MAXのとき、指定なしとして既定値13となる。
406@param[in] field    フィールド幅。
407@param[in] flags    書式フラグ。
408@return xの文字列表現
409
410C99では、末尾の0を取り除いても良いとあるので、
411このルーチンでは取り除くことにしている。
412*/
413Function FormatFloatA(x As Double, precision As DWord, field As DWord, flags As FormatFlags) As String
414    FormatFloatA = FormatFloatEx(AddressOf(FormatFloatA_Convert), x, precision, field, flags)
415End Function
416
417/*
418@brief  浮動小数点数をprintfの%a, %A(指数形式、十六進法)相当の変換で文字列化する関数。
419@author Egtra
420@date   2008/03/08
421@param[in,out] sb 書式化した文字列を追加するバッファ。
422@param[in] x    文字列化する浮動小数点数値。
423@param[in] precision    精度。小数点以下の桁数。DWORD_MAXのとき、指定なしとして既定値6となる。
424@param[in] field    フィールド幅。
425@param[in] flags    書式フラグ。
426*/
427Sub FormatFloatA(sb As System.Text.StringBuilder, x As Double, precision As DWord, field As DWord, ByRef flags As FormatFlags)
428    FormatFloatEx(sb, AddressOf(FormatFloatA_Convert), x, precision, field, flags)
429End Sub
430
431/*!
432@brief  浮動小数点数をprintfの%a, %A(指数形式、十六進法)相当の変換で文字列化する関数。
433@author Egtra
434@date   2008/03/07
435@param[in,out] sb 書式化した文字列を追加するバッファ。
436@param[in] x    文字列化する浮動小数点数値。
437@param[in] precision    精度。小数点以下の桁数。DWORD_MAXのとき、指定なしとして既定値13となる。
438@param[in] field    フィールド幅。
439@param[in,out] flags    書式フラグ。
440
441C99では、末尾の0を取り除いても良いとあるので、
442このルーチンでは取り除くことにしている。
443*/
444Sub FormatFloatA_Convert(sb As System.Text.StringBuilder, x As Double, precision As DWord, field As DWord, ByRef flags As FormatFlags)
445    If precision = DWORD_MAX Then
446        precision = 13
447    End If
448    Dim lastLength = sb.Length
449    Dim pqw = VarPtr(x) As *QWord
450
451    With sb
452        Dim sign = (GetQWord(pqw) And &H8000000000000000) As Boolean
453        pqw[0] And= &h7fffffffffffffff
454
455        AppendSign(sb, sign, flags)
456
457        If flags And BPrefix Then
458            .Append("&H")
459        Else
460            .Append("0X")
461        End If
462
463        Dim biasedExp = (GetQWord(pqw) >> 52) As DWord And &h7FF
464        Dim exp As Long
465        If biasedExp = 0 Then
466            If GetQWord(pqw) <> 0 Then
467                exp = -1022 '非正規化数への対応
468            Else
469                exp = 0
470            End If
471            .Append("0")
472        Else
473            exp = biasedExp - 1023
474            .Append("1")
475        End If
476
477        If precision > 0 Or (flags And Alt) Then
478            .Append(".")
479            Dim fraction = GetQWord(pqw) And &h000fffffffffffff
480            If precision < 13 Then
481                Dim dropped = 13 - precision
482                fraction >>= dropped * 4
483                FormatIntegerLX(sb, fraction, precision, 0, flags And Cap)
484            Else
485                FormatIntegerLX(sb, fraction, 13, 0, flags And Cap)
486                .Append(&h30, precision - 13)
487            End If
488        End If
489        FormatFloatG_RemoveLowDigit(sb, lastLength, flags)
490        .Append("P")
491        FormatIntegerD(sb, exp, 1, 0, Sign)
492        AdjustAlphabet(sb, flags, lastLength)
493    End With
494End Sub
495
496/*!
497@brief 先頭に符号もしくはその分の空白を出力する。FormatFloat用。
498@author Egtra
499@date 2007/10/23
500@param[in, out] sb  出力先
501@param[in] negative 符号
502@param[in, out] flags   フラグ。negative = Trueなら、Signを立てて返す。
503*/
504Sub AppendSign(sb As System.Text.StringBuilder, negative As Boolean, ByRef flags As FormatFlags)
505    With sb
506        If negative Then
507            .Append("-")
508            flags Or= Sign
509        Else
510            If flags And Sign Then
511                .Append("+")
512            ElseIf flags And Blank Then
513                .Append(" ")
514            End If
515        End If
516    End With
517End Sub
518
519/*!
520@brief  符号無し整数をprintfの%u(十進法表現)相当の変換で文字列化する関数。
521@author Egtra
522@date   2007/09/18
523@param[in]  x   文字列化する整数値。
524@param[in]  d   精度、最小限表示される桁数。DWORD_MAXのとき、指定なしとして、既定値1となる。
525@param[in]  field   フィールド幅。
526@param[in]  flags   書式フラグ。
527@return xの文字列表現
528*/
529Function FormatIntegerU(x As DWord, d As DWord, field As DWord, flags As FormatFlags) As String
530    Return FormatIntegerEx(TraitsIntegerU[0], x, d, field, flags And (Not (Sign Or Blank)))
531End Function
532
533/*!
534@brief  符号無し整数をprintfの%u(十進法表現)相当の変換で文字列化する関数。
535@author Egtra
536@date   2008/03/07
537@param[in,out] sb 書式化した文字列を追加するバッファ。
538@param[in]  x   文字列化する整数値。
539@param[in]  d   精度、最小限表示される桁数。DWORD_MAXのとき、指定なしとして、既定値1となる。
540@param[in]  field   フィールド幅。
541@param[in]  flags   書式フラグ。
542*/
543Sub FormatIntegerU(sb As System.Text.StringBuilder, x As DWord, d As DWord, field As DWord, flags As FormatFlags)
544    FormatIntegerEx(sb, TraitsIntegerU[0], x As QWord, d, field, flags And (Not (Sign Or Blank)))
545End Sub
546
547/*!
548@brief  FormatIntegerUのQWord版
549@author Egtra
550@date   2007/10/26
551*/
552Function FormatIntegerLU(x As QWord, d As DWord, field As DWord, flags As FormatFlags) As String
553    Return FormatIntegerEx(TraitsIntegerU[1], x, d, field, flags And (Not (Sign Or Blank)))
554End Function
555
556/*!
557@brief  FormatIntegerUのQWord版
558@author Egtra
559@date   2008/03/07
560@param[in,out] sb 書式化した文字列を追加するバッファ。
561@param[in]  x   文字列化する整数値。
562@param[in]  d   精度、最小限表示される桁数。DWORD_MAXのとき、指定なしとして、既定値1となる。
563@param[in]  field   フィールド幅。
564@param[in]  flags   書式フラグ。
565*/
566Sub FormatIntegerLU(sb As System.Text.StringBuilder, x As QWord, d As DWord, field As DWord, flags As FormatFlags)
567    FormatIntegerEx(sb, TraitsIntegerU[0], x, d, field, flags And (Not (Sign Or Blank)))
568End Sub
569
570/*!
571@brief  符号有り整数をprintfの%d(十進法表現)相当の変換で文字列化する関数。
572@author Egtra
573@date   2007/10/13
574@param[in]  x   文字列化する整数値。
575@param[in]  d   精度、最小限表示される桁数。DWORD_MAXのとき、指定なしとして、既定値1となる。
576@param[in]  field   フィールド幅。
577@param[in]  flags   書式フラグ。
578@return xの文字列表現
579*/
580Function FormatIntegerD(x As Long, d As DWord, field As DWord, flags As FormatFlags) As String
581    Return FormatIntegerEx(TraitsIntegerD[0], (x As Int64) As QWord, d, field, flags)
582End Function
583
584/*!
585@brief  符号有り整数をprintfの%d(十進法表現)相当の変換で文字列化する関数。
586@author Egtra
587@date   2008/03/07
588@param[in,out] sb 書式化した文字列を追加するバッファ。
589@param[in]  x   文字列化する整数値。
590@param[in]  d   精度、最小限表示される桁数。DWORD_MAXのとき、指定なしとして、既定値1となる。
591@param[in]  field   フィールド幅。
592@param[in]  flags   書式フラグ。
593*/
594Sub FormatIntegerD(sb As System.Text.StringBuilder, x As Long, d As DWord, field As DWord, flags As FormatFlags)
595    FormatIntegerEx(sb, TraitsIntegerD[0], (x As Int64) As QWord, d, field, flags)
596End Sub
597
598/*!
599@brief  FormatIntegerDのInt64版
600@author Egtra
601@date   2007/10/26
602*/
603Function FormatIntegerLD(x As Int64, d As DWord, field As DWord, flags As FormatFlags) As String
604    Return FormatIntegerEx(TraitsIntegerD[1], x As QWord, d, field, flags)
605End Function
606
607/*!
608@brief  FormatIntegerDのInt64版
609@author Egtra
610@date   2007/10/26
611*/
612Sub FormatIntegerLD(sb As System.Text.StringBuilder, x As Int64, d As DWord, field As DWord, flags As FormatFlags)
613    FormatIntegerEx(sb, TraitsIntegerD[1], x As QWord, d, field, flags)
614End Sub
615
616/*!
617@author Egtra
618@date   2007/10/26
619*/
620Dim TraitsIntegerU[1] As IntegerConvertTraits
621With TraitsIntegerU[0]
622    .Convert = AddressOf(IntegerU_Convert)
623    .Prefix = AddressOf(IntegerU_Prefix)
624End With
625
626With TraitsIntegerU[1]
627    .Convert = AddressOf(IntegerLU_Convert)
628    .Prefix = AddressOf(IntegerU_Prefix)
629End With
630
631/*!
632@author Egtra
633@date   2007/10/28
634*/
635Dim TraitsIntegerD[1] As IntegerConvertTraits
636With TraitsIntegerD[0]
637    .Convert = AddressOf(IntegerD_Convert)
638    .Prefix = AddressOf(IntegerD_Prefix)
639End With
640
641With TraitsIntegerD[1]
642    .Convert = AddressOf(IntegerLD_Convert)
643    .Prefix = AddressOf(IntegerD_Prefix)
644End With
645
646/*!
647@brief  負数を表すフラグ。FormatIntegerD, LDからIntegerDU_Prefixまでの内部処理用。
648@author Egtra
649@date   2007/10/26
650*/
651Const Minus = Reserved
652
653/*!
654@author Egtra
655@date   2007/10/26
656*/
657Function IntegerU_Convert(buf As *Char, xq As QWord, flags As FormatFlags) As DWord
658    Dim x = xq As DWord
659    Dim i = MaxSizeLO
660    While x <> 0
661        buf[i] = (x As Int64 Mod 10 + &h30) As Char 'Int64への型変換は#117対策
662        x \= 10
663        i--
664    Wend
665    Return i
666End Function
667
668/*!
669@brief  IntegerU_ConvertのQWord版
670@author Egtra
671@date   2007/10/26
672@bug    #117のため、現在Int64の最大値を超える値を正しく処理できない。
673*/
674Function IntegerLU_Convert(buf As *Char, x As QWord, flags As FormatFlags) As DWord
675    Dim i = MaxSizeLO
676    While x <> 0
677        buf[i] = (x As Int64 Mod 10 + &h30) As Char 'Int64への型変換は#117対策
678        x \= 10
679        i--
680    Wend
681    Return i
682End Function
683
684/*!
685@author Egtra
686@date   2007/10/26
687*/
688Function IntegerU_Prefix(x As QWord, flags As FormatFlags) As String
689End Function
690
691/*!
692@author Egtra
693@date   2007/10/28
694*/
695Function IntegerD_Convert(buf As *Char, xq As QWord, flags As FormatFlags) As DWord
696    Return IntegerU_Convert(buf, Abs((xq As DWord) As Long) As DWord, flags)
697End Function
698
699/*!
700@brief  IntegerD_ConvertのInt64版
701@author Egtra
702@date   2007/10/28
703*/
704Function IntegerLD_Convert(buf As *Char, x As QWord, flags As FormatFlags) As DWord
705    Return IntegerLU_Convert(buf, Abs(x As Int64) As QWord, flags)
706End Function
707
708/*!
709@author Egtra
710@date   2007/10/28
711*/
712Function IntegerD_Prefix(x As QWord, flags As FormatFlags) As String
713    If (x As Int64) < 0 Then
714        IntegerD_Prefix = "-"
715    ElseIf flags And Sign Then
716        IntegerD_Prefix = "+"
717    ElseIf flags And Blank Then
718        IntegerD_Prefix = " "
719    End If
720End Function
721
722
723
724/*!
725@brief  QWordの最大値の八進法表現1777777777777777777777の文字数 - 1 + 1。IntegerO_Convert内で使用。
726@author Egtra
727@date   2007/10/26
728上の式で1を加えているのは、八進接頭辞の分。
729*/
730Const MaxSizeLO = 22
731
732/*!
733@author Egtra
734@date   2007/10/22
735*/
736Dim TraitsIntegerO[1] As IntegerConvertTraits
737With TraitsIntegerO[0]
738    .Convert = AddressOf(IntegerO_Convert)
739    .Prefix = AddressOf(IntegerO_Prefix)
740End With
741
742With TraitsIntegerO[1]
743    .Convert = AddressOf(IntegerLO_Convert)
744    .Prefix = AddressOf(IntegerO_Prefix)
745End With
746
747/*!
748@brief  符号無し整数をprintfの%o(八進法表現)相当の変換で文字列化する関数。
749@author Egtra
750@date   2007/10/19
751@param[in]  x   文字列化する整数値。
752@param[in]  d   精度、最小限表示される桁数。DWORD_MAXのとき、指定なしとして、既定値1となる。
753@param[in]  field   フィールド幅。
754@param[in]  flags   書式フラグ。
755@return xの文字列表現
756*/
757Function FormatIntegerO(x As DWord, d As DWord, field As DWord, flags As FormatFlags) As String
758    Return FormatIntegerEx(TraitsIntegerO[0], x, d, field, flags)
759End Function
760
761/*!
762@brief  FormatIntegerOのQWord版。
763@author Egtra
764@date   2007/10/26
765*/
766Function FormatIntegerLO(x As QWord, d As DWord, field As DWord, flags As FormatFlags) As String
767    Return FormatIntegerEx(TraitsIntegerO[1], x, d, field, flags)
768End Function
769
770/*!
771@author Egtra
772@date   2007/10/22
773*/
774Function IntegerO_Convert(buf As *Char, xq As QWord, flags As FormatFlags) As DWord
775    Dim x = xq As DWord
776    Dim i = MaxSizeLO
777    While x <> 0
778        buf[i] = ((x And &o7) + &h30) As Char
779        x >>= 3
780        i--
781    Wend
782    If flags And Alt Then
783        buf[i] = &h30
784        i--
785    End If
786    Return i
787End Function
788
789/*!
790@brief  IntegerO_ConvertのQWord版。
791@author Egtra
792@date   2007/10/26
793*/
794Function IntegerLO_Convert(buf As *Char, x As QWord, flags As FormatFlags) As DWord
795    Dim i = MaxSizeLO
796    While x <> 0
797        buf[i] = ((x And &o7) + &h30) As Char
798        x >>= 3
799        i--
800    Wend
801    If flags And Alt Then
802        buf[i] = &h30
803        i--
804    End If
805    Return i
806End Function
807
808/*!
809@author Egtra
810@date   2007/10/22
811@note   #フラグ (Alt)の処理は、IntegerO/LO_Convert内で行うので、ここで処理することはない。
812*/
813Function IntegerO_Prefix(x As QWord, flags As FormatFlags) As String
814    If flags And BPrefix Then
815        If x <> 0 Then
816            IntegerO_Prefix = "&O"
817        End If
818    End If
819End Function
820
821/*!
822@author Egtra
823@date   2007/10/24
824*/
825Dim TraitsIntegerX[1] As IntegerConvertTraits
826With TraitsIntegerX[0]
827    .Convert = AddressOf(IntegerX_Convert)
828    .Prefix = AddressOf(IntegerX_Prefix)
829End With
830
831With TraitsIntegerX[1]
832    .Convert = AddressOf(IntegerLX_Convert)
833    .Prefix = AddressOf(IntegerX_Prefix)
834End With
835
836/*!
837@brief  整数をprintfの%x, %X(十六進法)相当の変換で文字列化する関数。
838@author Egtra
839@date   2007/10/19
840@param[in]  x   文字列化する整数値。
841@param[in]  d   精度、最小限表示される桁数。DWORD_MAXのとき、指定なしとして、既定値1となる。
842@param[in]  field   フィールド幅。
843@param[in]  flags   書式フラグ。
844@return xの文字列表現
845*/
846Function FormatIntegerX(x As DWord, d As DWord, field As DWord, flags As FormatFlags) As String
847    Return FormatIntegerEx(TraitsIntegerX[0], x, d, field, flags)
848End Function
849
850/*!
851@brief  FormatIntegerXのQWord版。
852@author Egtra
853@date   2007/10/22
854*/
855Function FormatIntegerLX(x As QWord, d As DWord, field As DWord, flags As FormatFlags) As String
856    Return FormatIntegerEx(TraitsIntegerX[1], x, d, field, flags)
857End Function
858
859/*!
860@brief  FormatIntegerXのQWord, StringBuilder版。
861@author Egtra
862@date   2008/03/07
863*/
864Sub FormatIntegerLX(sb As System.Text.StringBuilder, x As QWord, d As DWord, field As DWord, flags As FormatFlags)
865    FormatIntegerEx(sb, TraitsIntegerX[1], x, d, field, flags)
866End Sub
867
868/*
869@brief  0からFまでの文字を収めた表
870@author egtra
871*/
872Dim HexadecimalTable[&h10] = [&h30, &h31, &h32, &h33, &h34, &h35, &h36, &h37, &h38, &h39, &h41, &h42, &h43, &h44, &h45, &h46] As Byte
873
874/*!
875@author Egtra
876@date   2007/10/22
877*/
878Function IntegerX_Convert(buf As *Char, xq As QWord, flags As FormatFlags) As DWord
879    Dim i = MaxSizeLO
880    Dim x = xq As DWord
881    While x <> 0
882        buf[i] = HexadecimalTable[x And &h0f]
883        x >>= 4
884        i--
885    Wend
886    Return i
887End Function
888
889/*!
890@brief  IntegerX_ConvertのQWord版。
891@author Egtra
892@date   2007/10/22
893*/
894Function IntegerLX_Convert(buf As *Char, x As QWord, flags As FormatFlags) As DWord
895    Dim i = MaxSizeLO
896    While x <> 0
897        buf[i] = HexadecimalTable[x And &h0f]
898        x >>= 4
899        i--
900    Wend
901    Return i
902End Function
903
904/*!
905@author Egtra
906@date   2007/10/24
907*/
908Function IntegerX_Prefix(x As QWord, flags As FormatFlags) As String
909    If x <> 0 Then
910        If flags And Alt Then
911            IntegerX_Prefix = "0X"
912        ElseIf flags And BPrefix Then
913            IntegerX_Prefix = "&H"
914        End If
915    End If
916End Function
917
918/*!
919@brief  FormatIntegerExへ渡す変換特性を表す構造体型。
920@author Egtra
921@date   2007/10/22
922
923FormatIntegerの都合上、このファイル内で宣言しているIntegerConvertTraits型の
924変数は全て配列となっている;[0]が32ビット変換、[1]が64ビット変換である。
925*/
926Type IntegerConvertTraits
927    '!変換を行う関数へのポインタ。
928    Convert As *Function(buf As *Char, x As QWord, flags As FormatFlags) As DWord
929    '!接頭辞を取得する関数へのポインタ。
930    Prefix As *Function(x As QWord, flags As FormatFlags) As String
931End Type
932
933/*!
934@brief  整数変換全てを行う関数。
935@author Egtra
936@date 2007/10/22
937@param[in] tr   特性情報。
938@param[in] x    変換元の数値。
939@param[in] d    精度。ここでは最低限出力する桁数。
940@param[in] field    フィールド幅。
941@param[in] flags    フラグ。
942@return 変換された文字列
943*/
944Function FormatIntegerEx(ByRef tr As IntegerConvertTraits, x As QWord, d As DWord, field As DWord, flags As FormatFlags) As String
945    Dim sb = New System.Text.StringBuilder(32)
946    FormatIntegerEx(sb, tr, x, d, field, flags)
947    FormatIntegerEx = sb.ToString
948End Function
949
950/*!
951@brief  整数変換全てを行う関数。これを雛形とし、形式毎の差異はIntegerConvertTraitsで表現する。
952@author Egtra
953@date 2008/03/06
954@param[in,out] sb 書式化した文字列を追加するバッファ。
955@param[in] tr   特性情報。
956@param[in] x    変換元の数値。
957@param[in] d    精度。ここでは最低限出力する桁数。
958@param[in] field    フィールド幅。
959@param[in] flags    フラグ。
960*/
961Sub FormatIntegerEx(sb As System.Text.StringBuilder, ByRef tr As IntegerConvertTraits, x As QWord, d As DWord, field As DWord, flags As FormatFlags)
962    If d = DWORD_MAX Then
963        d = 1
964    Else
965        '精度が指定されているとき、ゼロフラグは無視される。
966        '仕様上、左揃えのときも無視されるが、それはAdjustFieldWidthが行ってくれる。
967        flags And= Not Zero
968    End If
969
970    Dim lastLength = sb.Length
971
972    With sb
973        Dim prefixFunc = tr.Prefix
974        Dim prefix = prefixFunc(x, flags)
975        sb.Append(prefix)
976
977        Dim prefixLen = 0 As DWord
978        If String.IsNullOrEmpty(prefix) = False Then
979            prefixLen = prefix.Length As DWord
980        End If
981
982        'バッファの量は最も必要文字数の多くなるUInt64の8進法変換にあわせている
983        Dim buf[MaxSizeLO] As Char
984        Dim convertFunc = tr.Convert
985        Dim bufStartPos = convertFunc(buf, x, flags)
986
987        Dim len = (MaxSizeLO - bufStartPos) As Long
988        If len < 0 Then
989            Debug
990        End If
991        If len < d Then
992            .Append(&h30 As Char, d - len)
993        End If
994        .Append(buf, bufStartPos + 1, len)
995        AdjustFieldWidth(sb, field, flags And (Not (Sign Or Blank)), prefixLen, lastLength)
996        AdjustAlphabet(sb, flags, lastLength)
997    End With
998End Sub
999
1000/*!
1001@brief 浮動小数点数変換全てを行う関数。これを雛形とし、実際の変換関数は引数converterで与える。
1002@author Egtra
1003@date 2008/03/07
1004@param[in,out] sb 書式化した文字列を追加するバッファ。
1005@param[in] converter 変換関数。
1006@param[in] x 変換元の数値。
1007@param[in] d 精度。ここでは最低限出力する桁数。
1008@param[in] field フィールド幅。
1009@param[in] flags フラグ。
1010*/
1011Sub FormatFloatEx(sb As System.Text.StringBuilder, converter As FormatFloatProc, x As Double, precision As DWord, field As DWord, flags As FormatFlags)
1012    Dim lastLength = sb.Length
1013    If Math.IsNaN(x) Then
1014        sb.Append("NAN")
1015        AdjustAlphabet(sb, flags, lastLength)
1016    ElseIf Math.IsInf(x) Then
1017        AppendSign(sb, (GetQWord(VarPtr(x)) >> 63) As Boolean, flags)
1018        sb.Append("INFINITY")
1019        AdjustAlphabet(sb, flags, lastLength)
1020    Else
1021        converter(sb, x, precision, field, flags)
1022    End If
1023    AdjustFieldWidth(sb, field, flags, 0, lastLength)
1024End Sub
1025
1026
1027/*!
1028@brief 浮動小数点数変換全てを行う関数。
1029@author Egtra
1030@date 2008/03/08
1031@param[in] converter 変換関数。
1032@param[in] x 変換元の数値。
1033@param[in] d 精度。ここでは最低限出力する桁数。
1034@param[in] field フィールド幅。
1035@param[in] flags フラグ。
1036@return 変換された文字列。
1037*/
1038Function FormatFloatEx(converter As FormatFloatProc, x As Double, precision As DWord, field As DWord, flags As FormatFlags) As String
1039    Dim sb = New System.Text.StringBuilder
1040    FormatFloatEx(sb, converter, x, precision, field, flags)
1041    FormatFloatEx = sb.ToString
1042End Function
1043
1044/*!
1045@brief  書式化の仕上げとして、Capフラグが指定されていないときに小文字化する作業を行う。
1046@author Egtra
1047@date 2008/03/06
1048@param[in,out] sb 書式化した文字列を格納しているバッファ。
1049@param[in] flags フラグ。
1050@param[in] offset 書式変換した部分の開始位置。
1051*/
1052Sub AdjustAlphabet(sb As System.Text.StringBuilder, flags As FormatFlags, offset As Long)
1053    If (flags And Cap) = 0 Then
1054        Dim len = sb.Length
1055        Dim i As Long
1056        For i = offset To ELM(len)
1057            sb[i] = CType.ToLower(sb[i])
1058        Next
1059    End If
1060End Sub
1061
1062/*!
1063@brief  書式化の仕上げとして、変換部分がフィールド幅まで満たされるように空白などを挿入する。
1064@author Egtra
1065@date   2007/10/13
1066@param[in,out] sb   対象文字列
1067@param[in] field    フィールド幅
1068@param[in] hasSign  符号を持っている(負の値か)か否か
1069@param[in] flags    フラグ
1070@param[in] prefixLen    (あれば)接頭辞の文字数。ゼロ埋めする際、この数だけ挿入位置を後ろにする。
1071@param[in] offset   変換した部分へのオフセット。AppendではなくInsertを行う際に用いられる。
1072sbが"-1"のように負符号を持っている場合は、呼出元でSignフラグ(またはBlank)を立てること。
1073*/
1074Sub AdjustFieldWidth(sb As System.Text.StringBuilder, field As DWord, flags As FormatFlags, prefixLen = 0 As DWord, offset = 0 As Long)
1075    With sb
1076        Dim len = .Length - offset
1077        If len < field Then
1078            Dim embeddedSize = field - len
1079            If flags And LeftSide Then
1080                .Append(&h20, embeddedSize)
1081            Else
1082                If (flags And Zero) <> 0 Then
1083                    offset += prefixLen
1084                    If (flags And Blank) Or (flags And Sign) Then
1085                        offset++
1086                    End If
1087                    .Insert(offset, String$(embeddedSize, "0"))
1088                Else
1089                    .Insert(offset, String$(embeddedSize, " "))
1090                End If
1091            End If
1092        End If
1093    End With
1094End Sub
1095
1096/*!
1097@brief  文字列をprintfの%s相当の変換で書式化する関数。
1098@author Egtra
1099@date   2007/10/27
1100@param[in]  x   文字列。
1101@param[in]  d   精度、最大の文字数。
1102@param[in]  field   フィールド幅。
1103@param[in]  flags   書式フラグ。
1104@return 書式化された文字列。
1105*/
1106Function FormatString(x As String, d As DWord, field As DWord, flags As FormatFlags) As String
1107    Imports System
1108    Dim sb = New System.Text.StringBuilder(
1109        Math.Max(Math.Min(x.Length As DWord, d), field) As Long + 1)
1110    FormatString(sb, x, d, field, flags)
1111    AdjustFieldWidth(sb, field, flags And LeftSide)
1112    FormatString = sb.ToString()
1113End Function
1114
1115/*!
1116@brief  文字列をprintfの%s相当の変換で書式化する関数。
1117@author Egtra
1118@date   2008/03/07
1119@param[in,out] sb 書式化した文字列を追加するバッファ。
1120@param[in] x 文字列。
1121@param[in] d 精度、最大の文字数。
1122@param[in] field フィールド幅。
1123@param[in] flags 書式フラグ。
1124@return 書式化された文字列。
1125*/
1126Sub FormatString(sb As System.Text.StringBuilder, x As String, d As DWord, field As DWord, flags As FormatFlags)
1127    Dim len = sb.Length
1128    sb.Append(x, 0, System.Math.Min(x.Length As DWord, d) As Long)
1129    AdjustFieldWidth(sb, field, flags And LeftSide, 0, len)
1130End Sub
1131
1132/*!
1133@brief  文字をprintfの%c相当の変換で書式化する関数。
1134@author Egtra
1135@date   2007/10/27
1136@param[in]  x   文字。
1137@param[in]  d   精度、最大の文字数。
1138@param[in]  field   フィールド幅。
1139@param[in]  flags   書式フラグ。
1140@return 書式化された文字列。
1141*/
1142Function FormatCharacter(x As Char, d As DWord, field As DWord, flags As FormatFlags) As String
1143    Dim sb = New System.Text.StringBuilder(field + 2)
1144    FormatCharacter(sb, x, d, field, flags)
1145    FormatCharacter = sb.ToString()
1146End Function
1147
1148/*!
1149@brief  文字列をprintfの%s相当の変換で書式化する関数。
1150@author Egtra
1151@date   2008/03/07
1152@param[in,out] sb 書式化した文字列を追加するバッファ。
1153@param[in] x 文字。
1154@param[in] d 精度、最大の文字数。
1155@param[in] field フィールド幅。
1156@param[in] flags 書式フラグ。
1157@return 書式化された文字列。
1158*/
1159Sub FormatCharacter(sb As System.Text.StringBuilder, x As Char, d As DWord, field As DWord, flags As FormatFlags)
1160    Dim len = sb.Length
1161    sb.Append(x)
1162    AdjustFieldWidth(sb, field, flags And LeftSide, 0, len)
1163End Sub
1164
1165/*!
1166@author Egtra
1167@date   2007/10/28
1168*/
1169TypeDef FormatFloatProc = *Sub(sb As System.Text.StringBuilder, x As Double, precision As DWord, fieldWidth As DWord, ByRef flags As FormatFlags)
1170
1171/*!
1172@brief  SPrintfから呼ばれる浮動小数点数用書式文字列化関数
1173@author Egtra
1174@date   2007/10/28
1175*/
1176Sub FormatFloat(s As System.Text.StringBuilder, formatProc As FormatFloatProc,
1177    param As Object, precision As DWord, field As DWord, flags As FormatFlags)
1178
1179    Dim x As Double
1180    Dim typeName = param.GetType().FullName
1181    If typeName = "System.Double" Then
1182        x = param As System.Double
1183    ElseIf typeName = "System.Single" Then
1184        x = param As System.Single
1185    End If
1186    FormatFloatEx(s, formatProc, x, precision, field, flags)
1187End Sub
1188
1189/*!
1190@brief  SPrintfから呼ばれる整数用書式文字列化関数
1191@author Egtra
1192@date   2007/10/28
1193*/
1194Sub FormatInteger(s As System.Text.StringBuilder, traits As *IntegerConvertTraits,
1195    param As Object, signed As Boolean, typeWidth As Long, precision As DWord, field As DWord, flags As FormatFlags)
1196
1197    Dim x As QWord
1198    Dim typeName = param.GetType().FullName
1199    If typeName = "System.UInt64" Then
1200        x = param As System.UInt64
1201    ElseIf typeName = "System.Int64" Then
1202        x = (param As System.Int64) As QWord
1203    ElseIf typeName = "System.UInt32" Then
1204        x = param As System.UInt32
1205    ElseIf typeName = "System.Int32" Then
1206        x = (param As System.Int32) As QWord
1207    ElseIf typeName = "System.UInt16" Then
1208        x = param As System.UInt16
1209    ElseIf typeName = "System.Int16" Then
1210        x = (param As System.Int16) As QWord
1211    ElseIf typeName = "System.UInt8" Then
1212        x = param As System.Byte
1213    ElseIf typeName = "System.Int8" Then
1214        x = (param As System.SByte) As QWord
1215    End If
1216    '一旦縮めた後、符号・ゼロ拡張させる。
1217    'また、64ビット整数なら64ビット変換Traitsを選択する。
1218    If signed Then
1219        If typeWidth = 1 Then
1220            traits = VarPtr(traits[1])
1221        ElseIf typeWidth = 0 Then
1222            x = (((x As DWord) As Long) As Int64) As QWord
1223        ElseIf typeWidth = -1 Then
1224            x = (((x As Word) As Integer) As Int64) As QWord
1225        ElseIf typeWidth = -2 Then
1226            x = (((x As Byte) As SByte) As Int64) As QWord
1227        End If
1228    Else
1229        If typeWidth = 1 Then
1230            traits = VarPtr(traits[1])
1231        ElseIf typeWidth = 0 Then
1232            x = x As DWord
1233        ElseIf typeWidth = -1 Then
1234            x = x As Word
1235        ElseIf typeWidth = -2 Then
1236            x = x As Byte
1237        End If
1238    End If
1239    FormatIntegerEx(s, ByVal traits, x, precision, field, flags)
1240End Sub
1241
1242'Format関数群ここまで
1243'----
1244
1245/*!
1246@brief  文字列から数値への変換。さらに変換に使われなかった文字列の位置を返す。
1247@author Egtra
1248@date   2007/11/11
1249@param[in] s    変換する文字
1250@param[out] p   変換に使われなかった部分の先頭を指すポインタ
1251@return 変換して得られた数値。変換できなければ0。
1252*/
1253Function StrToLong(s As *Char, ByRef p As *Char) As Long
1254    Dim negative As Boolean
1255    Dim i = 0 As Long
1256    If s[i] = &h2d Then 'Asc("-")
1257        i++
1258        negative = True
1259    End If
1260    Do
1261        Dim c = s[i]
1262        If Not CType.IsDigit(c) Then Exit Do
1263        StrToLong *= 10
1264        StrToLong += ((c As DWord) And &h0f) As Long
1265        i++
1266    Loop
1267    If negative Then
1268        StrToLong = -StrToLong
1269    End If
1270    p = VarPtr(s[i])
1271End Function
1272
1273/*!
1274@brief  フィールド幅、精度用の数値読取
1275@author Egtra
1276@date   2007/11/11
1277@param[in, out] fmt 読み途中の書式指定
1278@param[in] params
1279@param[in, out] paramsCount
1280@param[out] ret 読み取った数値。読み取られなかったときの値は不定。
1281@retval True    読取を行った
1282@retval False   行わなかった
1283fmt[0]が*のときにはparamsから1つ読み取る。
1284そうでなく、fmtに数字(先頭に-符号があっても可)が並んでいれば、それを読み取る。
1285*/
1286Function ReadInt(ByRef fmt As *Char, params As *Object, ByRef paramsCount As SIZE_T, ByRef ret As Long) As Boolean
1287    If fmt[0] = &h2a Then '*
1288        fmt = VarPtr(fmt[1]) 'po
1289        ret = params[paramsCount] As System.Int32
1290        paramsCount++
1291        ReadInt = True
1292    Else
1293        Dim p As *Char
1294        ret = StrToLong(fmt, p)
1295        If fmt <> p Then
1296            fmt = p
1297            ReadInt = True
1298        Else
1299            ReadInt = False
1300        End If
1301    End If
1302End Function
1303
1304/*!
1305@brief  フラグ指定の読み込み
1306@author Egtra
1307@date   2007/10/28
1308@param[in, out] fmt
1309@param[out] flags
1310@retval True    読み込みが完了した。
1311@retval False   読み取り中に文字列が終了した(ヌル文字が現れた)。
1312*/
1313Function ReadFlags(ByRef fmt As *Char, ByRef flags As FormatFlags) As Boolean
1314    ReadFlags = False
1315    Do
1316        Select Case fmt[0]
1317            Case &h23 '#
1318                flags Or= Alt
1319            Case &h30 '0
1320                flags Or= Zero
1321            Case &h20 '空白
1322                flags Or= Blank
1323            Case &h2b '+
1324                flags Or= Sign
1325            Case &h2d '-
1326                flags Or = LeftSide
1327            Case &h26 '&
1328                flags Or= BPrefix
1329            Case 0
1330                Exit Function
1331            Case Else
1332                Exit Do
1333        End Select
1334        fmt = VarPtr(fmt[1]) 'po
1335    Loop
1336    ReadFlags = True
1337End Function
1338
1339/*!
1340@brief  フィールド幅指定の読み込み
1341@author Egtra
1342@date   2007/10/29
1343@param[in, out] fmt
1344@param[in] params
1345@param[in, out] paramsCount
1346@param[out] fieldWidth
1347@param[in, out] flags
1348*/
1349Sub ReadFieldWidth(ByRef fmt As *Char, params As *Object, ByRef paramsCount As SIZE_T,
1350    ByRef fieldWidth As DWord, ByRef flags As FormatFlags)
1351    Dim t As Long
1352    If ReadInt(fmt, params, paramsCount, t) Then
1353        If t < 0 Then
1354            flags Or= LeftSide
1355            fieldWidth = -t As DWord
1356        Else
1357            fieldWidth = t As DWord
1358        End If
1359    Else
1360        fieldWidth = 0
1361    End If
1362End Sub
1363
1364/*!
1365@brief  精度の読み込み
1366@author Egtra
1367@date   2007/10/29
1368@param[in, out] fmt
1369@param[in] params
1370@param[in, out] paramsCount
1371@return 読み取った精度。指定がなかったときには、DWORD_MAX。
1372*/
1373Function ReadPrecision(ByRef fmt As *Char,
1374    params As *Object, ByRef paramsCount As SIZE_T) As DWord
1375
1376    If fmt[0] = &h2e Then '.
1377        fmt = VarPtr(fmt[1]) 'po
1378        Dim t As Long
1379        ReadPrecision = 0
1380        If ReadInt(fmt, params, paramsCount, t) Then
1381            If t > 0 Then
1382                ReadPrecision = t As DWord
1383            End If
1384        End If
1385    Else
1386        ReadPrecision = DWORD_MAX
1387    End If
1388End Function
1389
1390#ifdef _WIN64
1391Const PtrLength = 1
1392#else
1393Const PtrLength = 0
1394#endif
1395
1396/*!
1397@biref  長さ指定の読み込み
1398@author Egtra
1399@date   2007/10/29
1400@param[in, out] fmt
1401@param[out] lengthSpec
1402*/
1403Sub ReadLength(ByRef fmt As *Char, ByRef lengthSpec As Long)
1404    Do
1405        Select Case fmt[0]
1406            Case &h6c 'l
1407                lengthSpec++
1408            Case &h68 'h
1409                lengthSpec--
1410            Case &h6a 'j (u)intmax_t = QWord, Int64
1411                lengthSpec = 1
1412            Case &h74 't ptrdiff_t
1413                lengthSpec = PtrLength
1414            Case &h7a 'z (s)size_t
1415                lengthSpec = PtrLength
1416            Case &h70 'p VoidPtr 本来は変換指定子だが、ここで先読み
1417                lengthSpec = PtrLength
1418                Exit Sub 'fmtを進められると困るので、ここで脱出
1419            Case Else
1420                Exit Sub
1421        End Select
1422        fmt = VarPtr(fmt[1]) 'po
1423    Loop
1424End Sub
1425
1426/*!
1427@brief efg変換用に、精度が指定されていないときに既定の精度を設定する。
1428@auther Egtra
1429@date 2008/03/07
1430*/
1431Sub AdjustPrecision(ByRef precision As DWord)
1432    If precision = DWORD_MAX Then
1433        precision = 6
1434    End If
1435End Sub
1436
1437/*!
1438@biref  Cのsprintfのような書式文字列出力関数
1439@author Egtra
1440@date   2007/10/27
1441@param[in] format   書式文字列。詳細は開発Wiki参照。
1442@param[in, out] params  変換対象の配列。n = 0のときにはNULLも可。
1443@param[in] n    paramsの個数。
1444@return 書式化された文字列。
1445@todo   %nへの対応
1446*/
1447Function SPrintf(format As String, params As *Object, n As SIZE_T) As String
1448    Dim i = 0 As SIZE_T
1449    Dim paramsCount = 0 As SIZE_T
1450    Dim fmt = StrPtr(format)
1451    Dim s = New System.Text.StringBuilder
1452    Do
1453        Dim last = format.Length - (((fmt - StrPtr(format)) \ SizeOf (Char)) As LONG_PTR) As Long 'po
1454        Dim pos = ChrFind(fmt, last, &h25 As Char) As Long '&h25 = %
1455        If pos = -1 Then
1456            s.Append(fmt, 0, last)
1457            Exit Do
1458        End If
1459        '%以前の部分
1460        s.Append(fmt, 0, pos)
1461        fmt = VarPtr(fmt[pos + 1]) 'po
1462        'フラグの読取
1463        Dim flags = None As FormatFlags
1464        If ReadFlags(fmt, flags) = False Then
1465            Exit Do
1466        End If
1467        'フィールド幅
1468        Dim fieldWidth As DWord
1469        ReadFieldWidth(fmt, params, i, fieldWidth, flags)
1470        '精度
1471        Dim precision = ReadPrecision(fmt, params, i)
1472        '幅指定の読取
1473        Dim typeWidth As Long
1474        ReadLength(fmt, typeWidth)
1475
1476        Select Case fmt[0]
1477            Case &h64 'd
1478                FormatInteger(s, TraitsIntegerD, params[i], True, typeWidth, precision, fieldWidth, flags)
1479            Case &h69 'i
1480                FormatInteger(s, TraitsIntegerD, params[i], True, typeWidth, precision, fieldWidth, flags)
1481            Case &h75 'u
1482                FormatInteger(s, TraitsIntegerU, params[i], False, typeWidth, precision, fieldWidth, flags)
1483            Case &h4f 'O
1484                flags Or= Cap
1485                Goto *O
1486            Case &h6f 'o
1487            *O
1488                FormatInteger(s, TraitsIntegerO, params[i], False, typeWidth, precision, fieldWidth, flags)
1489            Case &h58 'X
1490                flags Or= Cap
1491                Goto *X
1492            Case &h78 'x
1493            *X
1494                FormatInteger(s, TraitsIntegerX, params[i], False, typeWidth, precision, fieldWidth, flags)
1495'現状ではVoidPtrを引数にする手段は無いはず
1496'           Case &h58 'p
1497'               FormatInteger(s, TraitsIntegerX, params[i], False, typeWidth, precision, fieldWidth, flags Or Cap)
1498            Case &h45 'E
1499                flags Or= Cap
1500                Goto *E
1501            Case &h65 'e
1502            *E
1503                AdjustPrecision(precision)
1504                FormatFloat(s, AddressOf(FormatFloatE_Convert), params[i], precision, fieldWidth, flags)
1505            Case &h46 'F
1506                flags Or= Cap
1507                Goto *F
1508            Case &h66 'f
1509            *F
1510                AdjustPrecision(precision)
1511                FormatFloat(s, AddressOf(FormatFloatF_Convert), params[i], precision, fieldWidth, flags)
1512            Case &h47 'G
1513                flags Or= Cap
1514                Goto *G
1515            Case &h67 'g
1516            *G
1517                AdjustPrecision(precision)
1518                FormatFloat(s, AddressOf(FormatFloatG_Convert), params[i], precision, fieldWidth, flags)
1519            Case &h41 'A
1520                flags Or= Cap
1521                Goto *A
1522            Case &h61 'a
1523            *A
1524                FormatFloat(s, AddressOf(FormatFloatA), params[i], precision, fieldWidth, flags)
1525            Case &h73 's
1526                FormatString(s, params[i] As String, precision, fieldWidth, flags)
1527            Case &h63 'c
1528                FormatCharacter(s, params[i] As BoxedStrChar, precision, fieldWidth, flags)
1529'           Case &h6e 'n
1530            Case &h25 '%
1531                s.Append(&h25 As Char)
1532                i--
1533            Case 0
1534                Exit Do
1535        End Select
1536        fmt = VarPtr(fmt[1]) 'po
1537        i++
1538    Loop
1539    SPrintf = s.ToString
1540End Function
1541
1542End Namespace 'Detail
1543
1544/*!
1545@brief  Cのsprintfのような書式化関数10引数版
1546@author Egtra
1547@date   2007/10/27
1548@param[in] format   書式指定
1549@param[in] paramN   引数
1550@return 書式化された文字列
1551*/
1552
1553Function SPrintf(format As String, param0 As Object,
1554    param1 As Object, param2 As Object, param3 As Object,
1555    param4 As Object, param5 As Object, param6 As Object,
1556    param7 As Object, param8 As Object, param9 As Object) As String
1557
1558    Dim params = VarPtr(param0) As *Object
1559
1560    SPrintf = ActiveBasic.Strings.Detail.SPrintf(format, params, 10)
1561End Function
1562
1563/*!
1564@brief  Cのsprintfのような書式化関数9引数版
1565@author Egtra
1566@date   2007/10/27
1567@param[in] format   書式指定
1568@param[in] paramN   引数
1569@return 書式化された文字列
1570*/
1571Function SPrintf(format As String, param0 As Object,
1572    param1 As Object, param2 As Object, param3 As Object,
1573    param4 As Object, param5 As Object, param6 As Object,
1574    param7 As Object, param8 As Object) As String
1575
1576    Dim params = VarPtr(param0) As *Object
1577
1578    SPrintf = ActiveBasic.Strings.Detail.SPrintf(format, params, 9)
1579End Function
1580
1581/*!
1582@brief  Cのsprintfのような書式化関数8引数版
1583@author Egtra
1584@date   2007/10/27
1585@param[in] format   書式指定
1586@param[in] paramN   引数
1587@return 書式化された文字列
1588*/
1589Function SPrintf(format As String, param0 As Object,
1590    param1 As Object, param2 As Object, param3 As Object,
1591    param4 As Object, param5 As Object, param6 As Object,
1592    param7 As Object) As String
1593
1594    Dim params = VarPtr(param0) As *Object
1595
1596    SPrintf = ActiveBasic.Strings.Detail.SPrintf(format, params, 8)
1597End Function
1598
1599/*!
1600@brief  Cのsprintfのような書式化関数7引数版
1601@author Egtra
1602@date   2007/10/27
1603@param[in] format   書式指定
1604@param[in] paramN   引数
1605@return 書式化された文字列
1606*/
1607Function SPrintf(format As String, param0 As Object,
1608    param1 As Object, param2 As Object, param3 As Object,
1609    param4 As Object, param5 As Object, param6 As Object) As String
1610
1611    Dim params = VarPtr(param0) As *Object
1612
1613    SPrintf = ActiveBasic.Strings.Detail.SPrintf(format, params, 7)
1614End Function
1615
1616/*!
1617@brief  Cのsprintfのような書式化関数6引数版
1618@author Egtra
1619@date   2007/10/27
1620@param[in] format   書式指定
1621@param[in] paramN   引数
1622@return 書式化された文字列
1623*/
1624Function SPrintf(format As String, param0 As Object,
1625    param1 As Object, param2 As Object, param3 As Object,
1626    param4 As Object, param5 As Object) As String
1627
1628    Dim params = VarPtr(param0) As *Object
1629
1630    SPrintf = ActiveBasic.Strings.Detail.SPrintf(format, params, 6)
1631End Function
1632
1633/*!
1634@brief  Cのsprintfのような書式化関数5引数版
1635@author Egtra
1636@date   2007/10/27
1637@param[in] format   書式指定
1638@param[in] paramN   引数
1639@return 書式化された文字列
1640*/
1641Function SPrintf(format As String, param0 As Object,
1642    param1 As Object, param2 As Object, param3 As Object,
1643    param4 As Object) As String
1644
1645    Dim params = VarPtr(param0) As *Object
1646
1647    SPrintf = ActiveBasic.Strings.Detail.SPrintf(format, params, 5)
1648End Function
1649
1650/*!
1651@brief  Cのsprintfのような書式化関数4引数版
1652@author Egtra
1653@date   2007/10/27
1654@param[in] format   書式指定
1655@param[in] paramN   引数
1656@return 書式化された文字列
1657*/
1658Function SPrintf(format As String, param0 As Object,
1659    param1 As Object, param2 As Object, param3 As Object) As String
1660
1661    Dim params = VarPtr(param0) As *Object
1662
1663    SPrintf = ActiveBasic.Strings.Detail.SPrintf(format, params, 4)
1664End Function
1665
1666/*!
1667@brief  Cのsprintfのような書式化関数3引数版
1668@author Egtra
1669@date   2007/10/27
1670@param[in] format   書式指定
1671@param[in] paramN   引数
1672@return 書式化された文字列
1673*/
1674Function SPrintf(format As String, param0 As Object,
1675    param1 As Object, param2 As Object) As String
1676
1677    Dim params = VarPtr(param0) As *Object
1678
1679    SPrintf = ActiveBasic.Strings.Detail.SPrintf(format, params, 3)
1680End Function
1681
1682/*!
1683@brief  Cのsprintfのような書式化関数2引数版
1684@author Egtra
1685@date   2007/10/27
1686@param[in] format   書式指定
1687@param[in] paramN   引数
1688@return 書式化された文字列
1689*/
1690Function SPrintf(format As String, param0 As Object,
1691    param1 As Object) As String
1692
1693    Dim params = VarPtr(param0) As *Object
1694
1695    SPrintf = ActiveBasic.Strings.Detail.SPrintf(format, params, 2)
1696End Function
1697
1698/*!
1699@brief  Cのsprintfのような書式化関数1引数版
1700@author Egtra
1701@date   2007/10/27
1702@param[in] format   書式指定
1703@param[in] paramN   引数
1704@return 書式化された文字列
1705*/
1706Function SPrintf(format As String, param0 As Object) As String
1707    Dim params = VarPtr(param0) As *Object
1708    SPrintf = ActiveBasic.Strings.Detail.SPrintf(format, params, 1)
1709End Function
1710
1711/*!
1712@brief  Cのsprintfのような書式化関数0引数版
1713@author Egtra
1714@date   2007/10/27
1715@param[in] format   書式指定
1716@param[in] paramN   引数
1717@return 書式化された文字列
1718*/
1719Function SPrintf(format As String) As String
1720    SPrintf = ActiveBasic.Strings.Detail.SPrintf(format, 0, 0)
1721End Function
1722
1723End Namespace 'Strings
1724End Namespace 'ActiveBasic
Note: See TracBrowser for help on using the repository browser.