| Path: | dcstringsprintf.f90 |
| Last Update: | Tue Sep 23 18:56:14 +0900 2008 |
| Authors: | Yasuhiro MORIKAWA, Eizi TOYODA |
| Version: | $Id: dcstringsprintf.f90,v 1.1.1.1 2008-09-23 09:56:14 morikawa Exp $ |
| Tag Name: | $Name: gtool5-20081007 $ |
| Copyright: | Copyright (C) GFD Dennou Club, 2000-2005. All rights reserved. |
| License: | See COPYRIGHT |
C の sprintf(3) のように文字列をフォーマットして返します。 ただし、実装は C の sprintf(3) とは大分違うのでご注意ください。
dc_string#CPrintf, dc_string#Printf のフォーマット引数に 用いられる指示子は % で始まります。種類は 以下の通りです。
| 指示子 : | 対応する引数 :: データの種類と出力形式 |
| %d, %D : | i(:) :: 整数データ (10 進数) を表示.
%2d や %04d のように'%' の後ろに数字を指定することで
出力する桁数を変更できます.
'%' の直後が 0 の場合は先頭に 0 を, そうでない場合は空白を埋めます.
|
| %o, %O : | i(:) :: 8 進数データを表示 |
| %x, %X : | i(:) :: 16 進数データを表示 |
| %f, %F : | d(:) :: 倍精度実数データを最大全桁数 80、小数部の桁数 40で表示 |
| %r, %R : | r(:) :: 単精度実数データを最大全桁数 80、小数部の桁数 40で表示 |
| %b, %B : | L(:) :: 論理データを 真:T、偽:F で表示 |
| %y, %Y : | L(:) :: 論理データを 真:yes、偽:no で表示 |
| %c, %C : | c1、c2、c3 :: 文字データ (変数) |
| %a, %A : | ca :: 文字データ (配列) |
文字データ (変数) 以外は、1つの型のデータをいくつでも与えることが可能です。 文字データ (変数) は c1、c2、c3 にそれぞれ 1 つづつの文字データしか与えることができません。 ca 引数を用いる場合は dc_string#StoA を併用すると便利です。
また、フォーマット指定子として %* を与えることで、 複数のデータを一度に出力することも可能です。 その場合、いくつのデータを一度に出力するかを n(:) に与える必要があります。
use dc_types, only: STRING
use dc_string, only: CPrintf
character(len = STRING) :: output, color="RED", size="Large"
integer, parameter :: n1 = 2, n2 = 3
integer :: int = 10, arrayI1(n1), arrayI2(n2), i
real :: arrayR(n1)
logical :: eq
do, i = 1, n1
arrayI1(i) = 123 * i ; arrayR(i) = 1.23 * i
enddo
do, i = 1, n2
arrayI2(i) = 345 * i
enddo
eq = (maxval(arrayI1) == minval(arrayI2))
output = CPrintf(fmt="color=%c size=%c int=%03d I1=%*d I2=%*04d R=%*r equal=%y", &
& c1=trim(color), c2=trim(size), i=(/int, arrayI1, arrayI2/), &
& r=(/arrayR/), L=(/eq/), n=(/n1, n2, n1/))
write(*,*) trim(output)
文字データ以外のものは基本的に1次元配列しか引数にとれないため、 多次元配列を出力したい場合には組込み関数である pack 関数を 用いると良いでしょう。以下にその例を記します。
use dc_types, only: STRING
use dc_string, only: CPrintf
character(len = STRING) :: output
integer :: i,j,k
integer, parameter :: n1 = 2, n2 = 3, n3 = 4
real :: array(n1,n2,n3)
do, i = 1, n1
do, j = 1, n2
do, k = 1, n3
array(i,j,k) = i * 0.1 + j * 1.0 + k * 10.0
enddo
enddo
enddo
output = CPrintf('array=<%*r>', &
& r=(/pack(array(:,:,:), .true.)/), n=(/size(array(:,:,:))/))
write(*,*) trim(output)
use dc_types, only: STRING
use dc_string, only: Printf
character(len = STRING) :: output, color="RED", size="Large"
integer, parameter :: n1 = 2, n2 = 3
integer :: int = 10, arrayI1(n1), arrayI2(n2), i
real :: arrayR(n1)
logical :: eq
do, i = 1, n1
arrayI1(i) = 123 * i ; arrayR(i) = 1.23 * i
enddo
do, i = 1, n2
arrayI2(i) = 345 * i
enddo
eq = (maxval(arrayI1) == minval(arrayI2))
! 装置番号 6 (標準出力) に直接出力する場合
call Printf(unit=6, &
& fmt="color=%c size=%c int=%03d I1=%*d I2=%*04d R=%*r equal=%y", &
& c1=trim(color), c2=trim(size), i=(/int, arrayI1, arrayI2/), &
& r=(/arrayR/), L=(/eq/), n=(/n1, n2, n1/))
! 文字列 output に渡す場合
call Printf(unit=output, &
& fmt="color=%c size=%c int=%03d I1=%*d I2=%*04d R=%*r equal=%y", &
& c1=trim(color), c2=trim(size), i=(/int, arrayI1, arrayI2/), &
& r=(/arrayR/), L=(/eq/), n=(/n1, n2, n1/))
write(*,*) trim(output)
| Subroutine : | |
| unit : | character(*), intent(out) |
| fmt : | character(*), intent(in) |
| i(:) : | integer, intent(in), optional |
| r(:) : | real, intent(in), optional |
| d(:) : | real(DP), intent(in), optional |
| L(:) : | logical, intent(in), optional |
| n(:) : | integer, intent(in), optional |
| c1 : | character(*), intent(in), optional |
| c2 : | character(*), intent(in), optional |
| c3 : | character(*), intent(in), optional |
| ca(:) : | character(*), intent(in), optional |
フォーマット文字列 fmt に従って変換された文字列を unit に返します。 第2引数 fmt には指示子を含む文字列を与えます。 指示子には「%」を用います。 % を用いたい場合は 「%%」と記述します。 指示子および用例に関しての詳細は dcstringsprintf.f90 を参照ください。
subroutine DCStringSPrintf(unit, fmt, i, r, d, L, n, c1, c2, c3, ca)
!
! フォーマット文字列 fmt に従って変換された文字列を unit に返します。
! 第2引数 fmt には指示子を含む文字列を与えます。
! 指示子には「<tt>%</tt>」を用います。
! <tt>%</tt> を用いたい場合は 「<tt>%%</tt>」と記述します。
! 指示子および用例に関しての詳細は dcstringsprintf.f90 を参照ください。
!
use dc_types, only: DP
implicit none
character(*), intent(out) :: unit
character(*), intent(in) :: fmt
integer, intent(in), optional:: i(:), n(:)
real, intent(in), optional:: r(:)
real(DP), intent(in), optional:: d(:)
logical, intent(in), optional:: L(:)
character(*), intent(in), optional:: c1, c2, c3
character(*), intent(in), optional:: ca(:)
! 上記配列引数のカウンタ
integer:: ni, nr, nd, nl, nc, na, nn
integer:: ucur ! unit に書かれた文字数
integer:: endp ! 既に処理された fmt の文字数
integer:: cur ! 現在着目中の文字は fmt(cur:cur) である
integer:: ptr ! fmt から検索をするときに使用
integer:: exp_ptr ! fmt から数値の指数部を検索をするときに使用
integer:: minus_ptr ! '-' を検索する時に使用
integer:: repeat ! %数字 または %* から決定された繰返し数
integer:: m ! 1:repeat の範囲で動くループ変数
integer:: stat ! エラー処理
character(80):: cbuf ! read/write 文のバッファ
character(80):: exp_buf ! real/write 文の指数部のバッファ (実数型用)
character(80):: ibuf ! real/write 文のバッファ (整数型用)
integer:: len_ibuf ! ibuf の長さ
integer:: figs_ibuf ! ibuf の有効な桁数
logical:: int_zero_fill ! 先頭を 0 で埋めるかどうかを判定するフラグ (整数型用)
integer:: int_figs ! 整数型を出力する際の桁数 (整数型用)
continue
ni = 0
nr = 0
nd = 0
nl = 0
nc = 0
na = 0
nn = 0
unit = ""
ucur = 0
endp = 0
int_figs = 0
int_zero_fill = .false.
MainLoop: do
cur = endp + 1
if (cur > len(fmt)) exit MainLoop
!
! リテラルに転写できる文字列 fmt(cur:endp-1) を発見処理
!
endp = cur - 1 + scan(fmt(cur: ), '%')
if (endp > cur) then
call append(unit, ucur, fmt(cur:endp-1), stat)
if (stat /= 0) exit MainLoop
else if (endp == cur - 1) then
call append(unit, ucur, fmt(cur: ), stat)
exit MainLoop
endif
!
! % から書式指定文字までを fmt(cur:endp) とする
!
cur = endp + 1
endp = cur - 1 + scan(fmt(cur: ), 'DdOoXxFfRrBbYySsCcAa%')
if (endp < cur) then
call append(unit, ucur, fmt(cur-1: ), stat)
exit MainLoop
endif
cbuf = fmt(cur:endp-1)
!
! %* がある場合、n(:) に渡された数から繰り返し回数を取得
!
if (cbuf(1:1) == '*') then
nn = nn + 1
if (nn > size(n)) then
repeat = 1
else
repeat = n(nn)
endif
ibuf = cbuf(2:)
else
repeat = 1
ibuf = cbuf
! else if (cbuf == '') then
! repeat = 1
! else
! ptr = verify(cbuf, " 0123456789")
! if (ptr > 0) cbuf(ptr: ) = " "
! read(cbuf, "(I80)", iostat=ptr) repeat
endif
!
! %2d や %04d のように '%' の後ろに数字が指定され、
! かつ d (整数型変数の表示) の場合には先頭に空白
! または 0 を埋める.
!
if (scan(ibuf(1:1),'1234567890') > 0) then
if (ibuf(1:1) == '0') then
int_zero_fill = .true.
else
int_zero_fill = .false.
end if
read(unit=ibuf, fmt="(i80)") int_figs
else
int_figs = 0
int_zero_fill = .false.
endif
PercentRepeat: do, m = 1, repeat
if (m > 1) then
call append(unit, ucur, ", ", stat)
if (stat /= 0) exit MainLoop
endif
select case(fmt(endp:endp))
case('d', 'D')
if (.not. present(i)) cycle MainLoop
ni = ni + 1
if (ni > size(i)) cycle MainLoop
write(ibuf, "(i20)") i(ni)
len_ibuf = len(trim(adjustl(ibuf)))
figs_ibuf = verify(ibuf, ' ')
cbuf = ' '
if (int_figs > len_ibuf) then
minus_ptr = scan(ibuf, '-')
if (int_zero_fill) then
if (minus_ptr /= 0) then
len_ibuf = len_ibuf - 1
figs_ibuf = figs_ibuf + 1
cbuf(1:int_figs-len_ibuf) = '-0000000000000000000'
else
cbuf(1:int_figs-len_ibuf) = '00000000000000000000'
end if
end if
cbuf(int_figs-len_ibuf+1:) = ibuf(figs_ibuf:20)
else
cbuf = ibuf(figs_ibuf:20)
end if
call append(unit, ucur, trim(cbuf), stat)
if (stat /= 0) exit MainLoop
case('o', 'O')
if (.not. present(i)) cycle MainLoop
ni = ni + 1
if (ni > size(i)) cycle MainLoop
write(cbuf, "(o20)") i(ni)
call append(unit, ucur, trim(adjustl(cbuf)), stat)
if (stat /= 0) exit MainLoop
case('x', 'X')
if (.not. present(i)) cycle MainLoop
ni = ni + 1
if (ni > size(i)) cycle MainLoop
write(cbuf, "(z20)") i(ni)
call append(unit, ucur, trim(adjustl(cbuf)), stat)
if (stat /= 0) exit MainLoop
case('f', 'F')
if (.not. present(d)) cycle MainLoop
nd = nd + 1
if (nd > size(d)) cycle MainLoop
write(cbuf, "(g80.40)") d(nd)
cbuf = adjustl(cbuf)
exp_ptr = verify(cbuf, ' 1234567890-+.', back=.TRUE.)
exp_buf = ' '
if (exp_ptr > 0) then
exp_buf = cbuf(exp_ptr: )
cbuf(exp_ptr: ) = " "
end if
ptr = verify(cbuf, " 0", back=.TRUE.)
if (ptr > 0) cbuf(ptr+1: ) = " "
cbuf = trim(cbuf) // trim(exp_buf)
call append(unit, ucur, trim(adjustl(cbuf)), stat)
if (stat /= 0) exit MainLoop
case('r', 'R')
if (.not. present(r)) cycle MainLoop
nr = nr + 1
if (nr > size(r)) cycle MainLoop
write(cbuf, "(g80.40)") r(nr)
cbuf = adjustl(cbuf)
exp_ptr = verify(cbuf, ' 1234567890-+.', back=.TRUE.)
exp_buf = ' '
if (exp_ptr > 0) then
exp_buf = cbuf(exp_ptr: )
cbuf(exp_ptr: ) = " "
end if
ptr = verify(cbuf, " 0", back=.TRUE.)
if (ptr > 0) cbuf(ptr+1: ) = " "
cbuf = trim(cbuf) // trim(exp_buf)
call append(unit, ucur, trim(adjustl(cbuf)), stat)
if (stat /= 0) exit MainLoop
case('b', 'B')
if (.not. present(L)) cycle MainLoop
nl = nl + 1
if (nl > size(L)) cycle MainLoop
write(cbuf, "(L1)") L(nl)
call append(unit, ucur, trim(adjustl(cbuf)), stat)
if (stat /= 0) exit MainLoop
case('y', 'Y')
if (.not. present(L)) cycle MainLoop
nl = nl + 1
if (nl > size(L)) cycle MainLoop
if (L(nl)) then
call append(unit, ucur, "yes", stat)
if (stat /= 0) exit MainLoop
else
call append(unit, ucur, "no", stat)
if (stat /= 0) exit MainLoop
endif
case('c', 'C')
nc = nc + 1
if (nc == 1) then
if (.not. present(c1)) cycle PercentRepeat
call append(unit, ucur, c1, stat)
if (stat /= 0) exit MainLoop
else if (nc == 2) then
if (.not. present(c2)) cycle PercentRepeat
call append(unit, ucur, c2, stat)
if (stat /= 0) exit MainLoop
else if (nc == 3) then
if (.not. present(c3)) cycle PercentRepeat
call append(unit, ucur, c3, stat)
if (stat /= 0) exit MainLoop
endif
case('a', 'A')
if (.not. present(ca)) cycle MainLoop
na = na + 1
if (na > size(ca)) cycle MainLoop
call append(unit, ucur, trim(adjustl(ca(na))), stat)
if (stat /= 0) exit MainLoop
case('%')
call append(unit, ucur, '%', stat)
if (stat /= 0) exit MainLoop
end select
enddo PercentRepeat
enddo MainLoop
return
contains
subroutine append(unitx, ucur, val, stat)
!
! unitx に val を付加。その際、unitx がその最大文字列長を越えた場合
! には stat = 2 を返す。
!
character(*), intent(inout):: unitx ! 最終的に返される文字列
integer, intent(inout):: ucur ! unitx の文字数
character(*), intent(in) :: val ! unitx に付加される文字列
integer, intent(out) :: stat ! ステータス
integer :: wrsz ! val の文字列
continue
! unitx の最大長を越えた場合には stat = 2 を返す。
if (ucur >= len(unitx)) then
stat = 2
! 正常時の処理
else
! unitx の長さを越えた場合も考慮して unitx に val を付加する。
wrsz = min(len(val), len(unitx) - ucur)
unitx(1+ucur: wrsz+ucur) = val(1: wrsz)
ucur = ucur + wrsz
stat = 0
if (wrsz < len(val)) stat = 1
endif
end subroutine append
end subroutine DCStringSPrintf