SGF->テキスト変換で使ったプログラムの変換部分ソースです。
EUC前提で作ってあります。ソースもEUCで保存してください。
Webインターフェース部分は非公開です。
$sgf:sgfファイルを読み込んだもの。文字コードはEUC限定。SJISの2バイト目とかの処理はしてない。
変換後の文字列(タグなし)文字コードはEUC。
SGFのXX[yyy]をXX{nn}と変換し、$param[nn]=yyy とします。
つまり、SGFの構造部分と実データ部分を分離します。
その後、ゲーム情報部分と着手情報部分を分離し、
それぞれパラメータと実データを取り出し整形しています。
このソースは、自由に改変、流用していただいて結構です。
著作権を放棄はしませんが、主張はしません。
当然、このプログラムを使って起きた問題に対して責任は取りません。
問い合わせ先:mailto:hebotaro@t-factory.jp
#! /usr/bin/perl # SGF -> テキスト 変換 # (C)hebotaro@t-factory.jp %mv = ( 'B'=>'黒', 'W'=>'白' ); %xleg = ( 'a'=>'1','b'=>'2','c'=>'3','d'=>'4','e'=>'5', 'f'=>'6','g'=>'7','h'=>'8','i'=>'9','j'=>'10', 'k'=>'11','l'=>'12','m'=>'13','n'=>'14','o'=>'15', 'p'=>'16','q'=>'17','r'=>'18','s'=>'19' ); %yleg = ( 'a'=>'一','b'=>'二','c'=>'三','d'=>'四','e'=>'五', 'f'=>'六','g'=>'七','h'=>'八','i'=>'九','j'=>'十', 'k'=>'十一','l'=>'十二','m'=>'十三','n'=>'十四','o'=>'十五', 'p'=>'十六','q'=>'十七','r'=>'十八','s'=>'十九' ); 1; sub sgf2text{ my( $sgf ) = @_; ## 改行、空白とっぱらい。 $sgf =~ s/\n/ /g; $sgf =~ s/\r//g; ##$sgf =~ s/\s//g; ## ## $sgf = $j->set(\$sgf)->euc; # sgf 実データ、構造分離 $struct = $sgf; $cnt = 0; while( $struct =~ m/\[((\\.|.)*?)\]/ ){ $param[$cnt] = $1; $struct =~ s/\[((\\.|.)*?)\]/\{$cnt\}/; $cnt ++; } $struct =~ s/\s//g; #print "$struct\n"; #foreach( @param ){ # $a = $j->set(\$_)->sjis; # print "$a\n"; #} # sgf 対局情報 / 着手情報分離 $struct =~ m/\(\;(([\\].|.)*?)\;(.*)\)/; $info = $1; $move = $3; # print "info=$info\n"; ## 対局情報 生成 # $msg = ''; # EV[名前] 大会名 $msg = getParam('EV', $info, $msg, '', "\n" ); # GN[対局名] 対局名 $msg = getParam('GN', $info, $msg, '', "\n" ); # RO[n] トーナメントや番碁などの第n回戦か[]内に記入します。 $msg = getParam('RO', $info, $msg, 'ラウンド:', "\n" ); # DT[YYYY-MM-DD] $msg = getParam('DT', $info, $msg, '対局日:', "\n" ); # PC[名前] 対局場所の名前を[]内に記入します。 $msg = getParam('PC', $info, $msg, '対局場所:', "\n" ); # PB[名前] 黒の対局者名を[]内に記入します。 $msg = getParam('PB', $info, $msg, '黒番:', " " ); # BR[棋力] 黒の対局者の棋力を[]内に記入します。 $msg = getParam('BR', $info, $msg, '', "\n" ); # PW[名前] 白の対局者名を[]内に記入します。 $msg = getParam('PW', $info, $msg, '白番:', " " ); # WR[棋力] 白の対局者の棋力を[]内に記入します。 $msg = getParam('WR', $info, $msg, '', "\n", ); # HA[n] 置き石数を半角数字で[]内に記入します。互先の場合は0と記入します。 $msg = getParam('HA', $info, $msg, '置き石:', "子\n" ); # KO[n] コミの値を半角数字[]内に記入します。 # 逆コミ(白からのコミ出し)の場合は負号(-)を付けます。 $msg = getParam('KO', $info, $msg, 'コミ:', "目\n" ); $msg = getParam('KM', $info, $msg, 'コミ:', "目\n" ); # RE[] # 対局結果を[]内に記入します。 0:持碁 # B+x:黒x目勝ち、W+x:白x目勝ち # B+R:黒中押勝ち、W+R:白中押勝ち # B+T:白時間切れ負け、W+T:黒時間切れ負け $wok = getParam('RE', $info, '', '', '' ); if( $wok =~ m/^0$/ ){ $msg .= '結果:引き分け' . "\n"; } elsif( $wok =~ m/^B\+R$/ ){ $msg .= '結果:黒中押勝ち' . "\n"; } elsif( $wok =~ m/^W\+R$/ ){ $msg .= '結果:白中押勝ち' . "\n"; } elsif( $wok =~ m/^B\+T$/ ){ $msg .= '結果:白時間切れ負け' . "\n"; } elsif( $wok =~ m/^W\+T$/ ){ $msg .= '結果:黒時間切れ負け' . "\n"; } elsif( $wok =~ m/^B\+([\d\.]*)$/ ){ $msg .= "結果:黒$1目勝ち" . "\n"; } elsif( $wok =~ m/^W\+([\d\.]*)$/ ){ $msg .= "結果:白$1目勝ち" . "\n"; } elsif( $wok =~ m/^B\+(.*)/ ){ $msg .= "結果:黒$1勝ち" . "\n"; } elsif( $wok =~ m/^W\+(.*)/ ){ $msg .= "結果:白$1勝ち" . "\n"; } elsif( $wok ne '' ){ $msg .= "結果:$wok" . "\n"; } # RU[ルール名] # 対局ルール名を[]内に記入します。 # SZ[x] 碁盤の路数を[]内に記入します。 $msg = getParam('SZ', $info, $msg, '碁盤サイズ:', "路盤\n" ); # 置き碁、盤面上コメント、AE(?) # AW[xy][xy] AB[xy][xy] LB[xy][xy] $setup = ''; $wok = $info . 'T'; undef @mvmv; while( $wok =~ /AB(\{(.*?)\})([A-Z\(\)\;])/ ){ $ppp = $1; @mvmv = ( @mvmv, getMoveList( $ppp ) ); $wok =~ s/AB(\{(.*?)\})([A-Z\(\)\;])/\3/; } $mvmv = join ',', @mvmv; $setup .= '置き石 黒:' . $mvmv . "\n" if $mvmv ne ''; undef @mvmv; while( $wok =~ /AW(\{(.*?)\})([A-Z\(\)\;])/ ){ @mvmv = ( @mvmv, getMoveList( $1 ) ); $wok =~ s/AW(\{(.*?)\})([A-Z\(\)\;])/\3/; } $mvmv = join ',', @mvmv; $setup .= '置き石 白:' . $mvmv . "\n" if $mvmv ne ''; undef @mvmv; while( $wok =~ /LB(\{(.*?)\})([A-Z\(\)\;])/ ){ @mvmv = ( @mvmv, getLabelList( $1 ) ); $wok =~ s/LB(\{(.*?)\})([A-Z\(\)\;])/\3/; } $mvmv = join ',', @mvmv; $setup .= 'ラベル:' . $mvmv . "\n" if $mvmv ne ''; $msg .= $setup; # C[] $msg = getParam('GC', $info, $msg, '対局コメント:', "\n" ); $msg = getParam('C', $info, $msg, 'コメント:', "\n" ); ## $msg = $j->set(\$msg)->sjis; ## print $msg; # 分岐削除 # 最初の)までを確保 if( $move =~ m/(.*?)\)/ ){ $main_move = $1; $move =~ s/(.*?)\)//; # 以降 () のペアを削除 while( $move =~ m/\(.*?\)/ ){ $move =~ s/\(.*?\)//; } # 残りを連結 $main_move .= $move; } else { $main_move = $move; } # print $main_move; @moves = split /\;/, $main_move; ## print @moves; $msg .= "\n-- 以降着手 --\n"; $cnt = 0; $paren = 0; ## $msg = ''; foreach( @moves ){ # ラベル処理 $wok = $_ . 'T'; undef @mvmv; while( $wok =~ /LB(\{(.*?)\})([A-Z\(\)\;])/ ){ @mvmv = ( @mvmv, getLabelList( $1 ) ); $wok =~ s/LB(\{(.*?)\})([A-Z\(\)\;])/\3/; } $lb = join ',', @mvmv; $lb = '(' . $lb . ')' if $lb ne ''; # コメント処理 if( m/C\{(\d+?)\}/ ){ $comment = ' ' . $param[$1]; $comment =~ s/\\([^\\])/$1/g; $comment =~ s/\\\\/\\/g; } else { $comment = ''; } $cnt++; if( m/((B|W))\{(\d+?)\}/ ){ $x = $1; ## print "$1,$2,$3,$4\n"; $msg .= "$mv{$1}$cnt手目 "; if( $param[$3] =~ m/([a-z])([a-z])/ ){ $msg .= "$xleg{$1}の$yleg{$2}$comment$lb\n"; } } } return $msg; } sub getParam{ my( $cd, $src, $msgs, $pre, $sa )=@_; my( $x ); if( ( $src =~ m/(;|\}|\()$cd\{(\d+?)\}/ ) || ( $src =~ m/^($cd)\{(\d+?)\}/ ) ) { $x = $param[$2]; $x =~ s/\\([^\\])/$1/g; $x =~ s/\\\\/\\/g; $msgs .= $pre . $x . $sa if $x ne ''; } return $msgs; } sub getMoveList{ my( $list )=@_; my( @lb ); undef @lb; while( $list =~ m/\{(\d+?)\}/ ){ # print "$1\n"; if( $param[$1] =~ m/([a-z])([a-z])/ ){ @lb = (@lb, "$xleg{$1}の$yleg{$2}" ); } $list =~ s/\{(\d+?)\}//; } return join ",", @lb; } sub getLabelList{ my( $list )=@_; my( @lb ); undef @lb; while( $list =~ m/\{(\d+?)\}/ ){ # print "$1\n"; if( $param[$1] =~ m/([a-z])([a-z]):(.+)/ ){ @lb = (@lb, "$3 $xleg{$1}の$yleg{$2}" ); } $list =~ s/\{(\d+?)\}//; } return join ",", @lb; }
以上。HeboTaro?
上記プログラムを sgf2txt.pl として保存。それのフィルタ用インターフェース。
Windows用(というか、最終的にSJISに変換してるだけ(笑))
#! /usr/bin/perl require 'sgf2txt.pl'; use Jcode; my $j = new Jcode; $sgf = ''; while(<>){$sgf.=$_;} $sgf = $j->set(\$sgf)->euc; $msg = &sgf2text( $sgf ); $msg = $j->set(\$msg)->sjis; print $msg;