タブの処理、detab,entab ― 2014年11月03日 17:29
これまでに説明していないプログラムについて、2,3回に分けて説明します。
まずは、detabとentab。
detabは、文字列中にタブが現れると空白に置き換えます。entabはこの逆で、 続く空白をタブで置き換えます。
detabは、下記の通り。
# detab.for -- convert tabs to equivalent of blanks
character getc
character c
integer tabpos
integer col, tabs(MAXLINE)
call settab(tabs) # set initial tab stops
col = 1
while (getc(c) != EOF)
if (c == TAB)
repeat {
call putc(BLANK)
col = col + 1
} until (tabpos(col,tabs) == YES)
else if (c == NEWLINE) {
call putc(NEWLINE)
col = 1
}
else {
call pudc(c)
col = col + 1
}
stop
end
settab()は、tabs配列に、タブ位置情報を作り出します。tabpos()は、現在タブ位置にあるか否かを判定します。 いずれも簡単なルーチンですが、くくり出すことでプログラムの見通しを良くするとともに、タブ位置情報の構造を隠蔽することに 役立っています。
settab()は以下の通り。
# settab.r4 -- set initial tab stops
subroutine settab(tabs)
integer mod
integer i, tabs(MAXLINE)
for (i = 1; i <= MAXLINE; i = i + 1)
if (mod(i,8) == 1)
tabs(i) = YES
else
tabs(i) = NO
return
end
tabpos()は以下の通り。
# tabpos.r4 -- return YES(.true.) if col is a tab stop
integer function tabpos(col,tabs)
integer col, tabs(MAXLINE)
if (col > MAXLINE)
tabpos = YES
else
tabpos = tabs(col)
return
end
detab,settab(),tabpos()のWatcom Fortran77版は、下記の通り。
c detab.for -- convert tabs to equivalent of blanks
program detab
integer*1 getc,c
integer col,tabpos,tabs(81) ! MAXLINE(81)
call settab(tabs) ! set initial tab stops
col = 1
while (getc(c) .ne. -1) do ! EOF(-1)
if (c .eq. 9) then ! TAB(9)
loop
call putc(32) ! BLANK(32)
col = col + 1
until (tabpos(col,tabs) .eq. 1) ! YES(1)
else if (c .eq. 10) then ! NEWLINE(10)
call putc(10) ! NEWLINE(10)
col = 1
else
call putc(c)
col = col + 1
endif
end while
stop
end
c settab.for -- set initial tab stops
subroutine settab(tabs)
integer i,mod,tabs(81) ! MAXLINE(81)
i = 1
while (i .le. 81) do ! MAXLINE(81)
if (mod(i,8) .eq. 1) then
tabs(i) = 1 ! YES(1)
else
tabs(i) = 0 ! NO(0)
endif
i = i + 1
end while
return
end
c tabpos.for -- return YES(1) if col is a tab stop
integer function tabpos(col,tabs)
integer col,tabs(81) ! MAXLINE(81)
if (col .gt. 81) then ! MAXLINE(81)
tabpos = 1 ! YES(1)
else
tabpos = tabs(col)
endif
return
end
entabは、下記の通り。
# entab -- replace blanks by tab and blanks
character getc,c
integer col,newcol,tabpos,tabs(MAXLINE)
call settab(tabs)
cal = 1
repeat {
newcol = col
while (getc(c) == BLANK) { # collect blanks
newcol = newcol + 1
if (tabpos(col,tabs) == YES) {
call putc(TAB)
cal = newcol
}
}
for ( ; col < newcol; col = col + 1)
call putc(BLANK) # output leftover blanks
if (c == EOF)
break
call putc(c)
if (c == NEWLINE)
col = 1
else
col = col + 1
}
stop
end
ここでも、settab()、tabpos()を使用して、タブ位置情報の構造をを隠蔽しています。
entabのWatcom Fortran77版は、下記の通り。
c entab -- replace blanks by tabs and blanks
program entab
integer*1 getc,c
logical tabpos, tabs(81) ! MAXLINE(81)
integer col, newcol
call settab(tabs)
col = 1
loop
newcol = col
while (getc(c) .eq. 32) do ! collect blanks BLANK(32)
newcol = newcol + 1
if (tabpos(newcol,tabs)) then
call putc(9) ! TAB(9)
col = newcol
endif
end while
while (col .lt. newcol) do ! output leftover blanks
call putc(32) ! BLANK(32)
col = col + 1
end while
if (c .eq. -1) then ! EOF(-1)
exit
endif
call putc(c)
if (c .eq. 10) then ! NEWLINE(10)
col = 1
else
col = col + 1
endif
end loop
stop
end
detab,entabの拡張 ― 2014年11月08日 21:45
detab,entabを拡張します。引数でタブ位置の情報を渡せるようにします。
detab 1 5 10 15 20
entab 2 6 11 18 30
さらに、
detab 1 +5
entab 5 +6
はじめのは、タブ位置をそのまま引数で渡します。次のは、最初の引数を元に、次の引数で、インクリメンタルにタブ位置を計算します。
最初の版settab2.forを示します。
c settab2.for -- set initial tab stops
subroutine settab(tabs)
integer tabs(81) ! MAXLINE(81)
integer*1 argv(81) ! MAXLINE(81)
integer argc,junk,t,tpos
integer ctoi,getarg,mod
do 10 tpos=1,81
tabs(tpos) = 0 ! NO(0)
10 continue
if (getarg(1,argv,81) .eq. -1) then ! MAXLINE(81) EOF(-1)
tpos = 1
while (tpos .le. 81) do ! MAXLINE(81)
if (mod(i,8) .eq. 1) then
tabs(i) = 1 ! YES(1)
endif
tpos = tpos + 1
end while
else
junk = 1
tpos = ctoi(argv,junk)
if ((tpos .gt. 0) .and. (tpos .le. 81)) then
tabs(tpos) = 1 ! YES(1)
end if
if (getarg(2,argv,81) .ne. -1) then ! MAXLINE(81)
if (argv(1) .eq. 43) then ! PLUS(43)
junk = 1
t = ctoi(argv,junk)
while (tpos .le. 81) do
tabs(tpos) = 1 ! YES(1)
tpos = tpos + t
end while
else
junk = 1
tpos = ctoi(argv,junk)
if ((tpos .gt. 0) .and. (tpos .le. 81)) then
tabs(tpos) = 1 ! YES(1)
end if
argc = 3
while (getarg(argc,argv,81) .ne. -1) do ! MAXLINE(81) EOF(-1)
junk = 1
tpos = ctoi(argv,junk)
if ((tpos .gt. 0) .and. (tpos .le. 81)) then ! MAXLINE(81)
tabs(tpos) = 1 ! YES(1)
end if
argc = argc + 1
end while
end if
else
tabs(tpos) = 1 ! YES(1)
endif
end if
return
end
見通しが悪く、長ったらしくなってしまいました。
ここで、下請けルーチン、インクリメンタル型の引数を確認するtabincr(),タブ位置が範囲に入っているかを調べるtabbound()を導入します。
tabincr()は以下の通り。
c tabincr.for -- return YES if incrimentsl tab set
integer function tabincr()
integer*1 argv(81) ! MAXLINE(81)
integer getarg
if (getarg(2,argv,81) .eq. -1) then ! MAXLINE(81) EOF(-1)
tabincr = 0 ! NO(0)
else
if (argv(1) .eq. 43) then ! PLUS(43)
tabincr = 1 ! YES(1)
else
tabincr = 0 ! NO(0)
end if
end if
return
end
tabbound()は以下の通り。
c tabbound -- check tab position boundary
integer function tabbound(tab,tabmin,tabmax)
integer tab,tabmin,tabmax
if ((tabmin .ge. tab) .and. (tab .le. tabmax)) then
tabbound = 1 ! YES(1)
else
tabbound = 0 ! NO(0)
end if
return
end
これらを使ったsettab()は以下の通り。
c settab3.for -- set initial tab stops
subroutine settab(tabs)
integer tabs(81) ! MAXLINE(81)
integer*1 argv(81) ! MAXLINE(81)
integer argc,junk,tincr,tpos
integer ctoi,getarg,mod,tabincr
do 10 tpos=1,81
tabs(tpos) = 0 ! NO(0)
10 continue
if (getarg(1,argv,81) .eq. -1) then ! MAXLINE(81) EOF(-1)
tpos = 1
while (tpos .le. 81) do ! MAXLINE(81)
if (mod(tpos,8) .eq. 1) then
tabs(tpos) = 1 ! YES(1)
endif
tpos = tpos + 1
end while
else
junk = 1
tpos = ctoi(argv,junk)
if (tabbound(tpos,1,81) .eq. 1) then ! YES(1)
if (tabincr() .eq. 1) then ! YES(1)
tabs(tpos) = 1 ! YES(1)
junk = getarg(2,argv,81) ! MAXLINE(81)
junk = 1
tincr = ctoi(argv,junk)
tpos = tpos + tincr
while (tpos .le. 81) do ! MAXLINE(81)
tabs(tpos) = 1 ! YES(1)
tpos = tpos + tincr
end while
else
argc = 2
while (getarg(argc,argv,81) .ne. -1) do ! MAXLINE(81) EOF(-1)
junk = 1
tpos = ctoi(argv,junk)
if (tabbound(tpos,1,81) .eq. 1) then ! MAXLINE(1) YES(1)
tabs(tpos) = 1 ! YES(1)
end if
argc = argc + 1
end while
end if
end if
end if
return
end
多少は見通しが良くなったでしょうか。
文書情報の圧縮と復元 ― 2014年11月16日 09:47
簡単な、圧縮、復元ツール、compress, expandです。
compressは、読み込んだ文字が繰り返されていたら、反復符号、繰り返し回数、文字に置き換え出力します。
compressは、下記の通り。
# compress.r4 -- compress standard input
character getc
character buf(MAXCHUNK),c,lastc
integer nrep,nsave
# must have RCODE > MAXCHUNK or RCODE = 0
nsave = 0
for (lastc = getc(lastc); lastc != EOF; lastc = c) {
for (nrep = 1; getc(c) == lastc; nrep = nrep + 1)
if (nrep >= MAXCHUNK) # xount repetitions
break
if (nrep < THRESH) # apppend short string
for ( ; nrep >0; nrep = nrep - 1) {
nsave = nsave + 1
buf(nsave) = lastc
if (nsave >= MAXCHANK)
call putbuf(buf,nsave)
}
else {
call putbuf(buf,nsave)
call putc(RCODE)
call putc(lastc)
call putc(nrep)
}
}
call putbuf(buf,nsave) # put last chunk
stop
end
putbufはbufにたまったnsave個の文字を書き出します。
# putbuf.r4 -- output buf(1)...buf(nsave), clear nsave
subroutine putbuf(buf,nsave)
character buf(MAXCHUNK)
integer nsave
integer i
if (nsave > 0) {
call putc(nsave)
for (i = 1; i <= nsave; i = i + 1)
call putc(buf(i)
}
nsave = 0
return
end
expandは、下記の通り。
# expand.r4 -- uncompress standard input
character getc
character c,code
while (getc(code) != EOF)
if (code == RCODE) { # expand repetition
if (getc(c) == EOF)
exit
if (getc(code) == EOF)
exit
while (code > 0)
call putc(c)
code = code - 1
}
else {
for ( ; code > 0; code = code - 1) {
if (getc(c) == EOF)
break
call putc(c)
}
if (c == EOF)
break
]
stop
end
compress,putbuf(),expandのWatcom Fortran77版は、下記の通り。
c compress -- compress standard input
program compress
integer*1 getc
integer*1 buf(10),c,lastc ! MAXCHANK(10)
integer nrep,nsave
! must have RCODE > MAXCHUNK or RCODE = 0
nsave = 0
lastc = getc(lastc)
while (lastc .ne. -1) do ! EOF(-1)
nrep = 1
while (getc(c) .eq. lastc) do
if (nrep .ge. 10) then ! MAXCHUNK(10)
exit
end if
nrep = nrep + 1
end while
if (nrep .lt. 5) then ! THRESH(5)
while (nrep .gt. 0) do
nsave = nsave + 1
buf(nsave) = lastc
if (nsave .ge. 10) then ! MAXCHUNK(10)
call putbuf(buf,nsave)
endif
nrep = nrep - 1
end while
else
call putbuf(buf,nsave)
call putc(35) ! #(35)
call putc(lastc)
call putc(nrep)
endif
lastc = c
end while
call putbuf(buf,nsave) ! put last chunk
stop
end
c putbuf.for -- output buf(1)...buf(nsave), clear nsave
subroutine putbuf(buf,nsave)
integer*1 buf(10) ! MAXCHUNK(10)
integer nsave
integer i
if (nsave .gt. 0) then
call putc(nsave)
i = 1
while (i .le. nsave) do
call putc(buf(i))
i = i + 1
end while
end if
nsave = 0
return
end
c expand.for -- uncompress standard input
program expand
integer*1 getc
integer*1 c,code
while (getc(code) .ne. -1) do ! EOF(-1)
if (code .eq. 35) then ! expand repetition RCODE(#(35))
if (getc(c) .eq. -1) then ! EOF(-1)
exit
end if
if (getc(code) .eq. -1) then ! EOF(-1)
exit
end if
while (code .gt. 0) do
call putc(c)
code = code - 1
end while
else
while (code .gt. 0) do ! expand chunk
if (getc(c) .eq. -1) then ! EOF(-1)
exit
end if
call putc(c)
code = code - 1
end while
if (c .eq. -1) then ! EOF(-1)
exit
end if
end if
end while
stop
end
暗号化 ― 2014年11月22日 21:39
暗号化、複合化プログラムcryptです。
入力文字と暗号化キーのXORをとることで暗号化しています。複合も同じです。
XORをとるには、組み込みのieorがありますが、ここでは、自作しました。
cryptのratfor版は下記の通り。
# crypt.r4 -- encrypt and decrypt
character getc,xor
character c,key(MAXKEY)
integer getarg
integer mod,i,keylen
keylen = getarg(1,key,MAXKEY)
if (keylen == EOF)
call error('usage: crypt key.')
for (i = 1; getc(c) != EOF; i = mode(i,keylen) + 1)
call putc(xor(c,key(i)))
stop
end
とても単純ですね。Watcom Fortran 77版は下記の通り。
c crypt.for -- encrypt and decrypt
program crypt
integer*1 getc
integer*1 c,key(11) ! MAXKEY(11)
integer getarg,ieor
integer i,keylen
keylen = getarg(1,key,11) ! MAXKEY(11)
if (keylen .eq. -1) then ! EOF(-1)
call error('usage: crypt key.')
end if
i = 1
while (getc(c) .ne. -1) do ! EOF(-1)
call putc(xor(c,key(i)))
i = mod(i,keylen) + 1
end while
stop
end
xor()は、以下の通り。下請けルーチンbin2bit()は、integer*1の正の数を8ビットに分割します。bit2bin()は、8ビットに分割された数をinteger*1の数に戻します。いずれのルーチンも、負の数には対応できません。
c xor.for -- bit width xor
integer*1 function xor(bin1,bin2)
integer*1 bin1, bin2
integer*1 bit2bin
integer bit1(8),bit2(8),bit3(8),i
call bin2bit(bin1,bit1)
call bin2bit(bin2,bit2)
do 10 i=1,8
if (((bit1(i) .eq. 1) .and. (bit2(i) .eq. 0)) .or.
1 ((bit1(i) .eq. 0) .and. (bit2(i) .eq. 1))) then
bit3(i) = 1
else
bit3(i) = 0
endif
10 continue
xor = bit2bin(bit3)
return
end
bin2bit()は、以下の通り。
c bin2bit.for -- convert bin to bit(i)
subroutine bin2bit(bin8,bit)
integer*1 bin8
integer bit(8)
integer abs,mod
integer i,b
b = bin8
do 10 i=1,8
bit(i) = abs(mod(b,2))
b = b / 2
10 continue
return
end
bit2bin()は、以下の通り。
c bit2bin.for -- convert bit(8) to bin
integer*1 function bit2bin(bit)
integer bit(8)
bit2bin = bit(8)*128 + bit(7)*64 + bit(6)*32 + bit(5)*16
1 + bit(4)*8 + bit(3)*4 + bit(2)*2 + bit(1)
return
end
実は、大きな落とし穴がありました。暗号化した文字が26、すなわち、0x1aになることがあります。 0x1aは、Windowsでは、テキストファイルの終わりのマークです。暗号化したファイルを読み込むと、0x1aのところで、 切れてしまいます。仕様なので、これ以上の事は追求しないことにしました。
最近のコメント