#00 [foussin分室] ... [<<:home (index.html)] ... [<<:top (top.cgi)]
....... [<:prev (アマチュア・スタイル!)] ... [here: jpway.pl] ... [フレーム解除]

アマチュア・スタイル!

[lib 01] サブルーチン・ライブラリ(関数)
jpway.pl 日本語処理ルーチン(要 jcode.pl or Jcode.pm)

updated: 2010/08/01 Sun 23:06 (JST-9)
©pablo foussin
※ jcode.pl は、歌代 和正氏の著作物です ( ftp://ftp.iij.ad.jp/pub/IIJ/dist/utashiro/perl/ )
※ Jcode.pm は、小飼 弾氏の著作物です ( http://openlab.ring.gr.jp/Jcode/ ) -> [ 参考サイト一覧 ] (無断リンク)

#01 能書き: 10 years later?
#02 能書き: about "jpway.pl"
#03 jpway.pl の主要関数
#04 jpway.pl の使用例(雛型)
#05 サンプル・スクリプト
#06 ダウンロード


[<:head #00] [here: #01] [>:next #02]

#01 能書き: 10 years later?

アマチュアによるプログラミングでは、グローバル化よりもローカル化を重視する。自分が住んでいる地域、母国語圏で役に立つプログラムでなければ、そこで暮らしている自分の役に立つわけがないのだから、当然の帰結となる。つまり日本人ユーザーなら、日本語関連の処理(主に文字化け対策)は避けて通れない面倒な障壁の 1つと言える。

ひと昔前なら、jperl を使うことで、その面倒を回避することができた。が、現在の最新版の Perl は jperl パッチを当てることはできない。その代わり、Perl5.8 では Unicode(utf8) が標準実装された。『 use utf8; 』とか『 use encoding sjis; 』と記述することで tr/あ-ん/ア-ン/; のような処理が可能になっている。

プラットフォームのデフォルトが、初めから utf8 の Linux ならば、Perl5.8 の日本語処理は jperl とほぼ同じ使い方が可能だ。しかし Windows の場合は、コマンドプロンプトの出力コードが『sjis』なので、コンソール出力時に『utf8 → sjis』の変換が必要になる。この変換を行うためには、PerlIO.pm によって機能強化された『3引数open』や『binmode』の使い方を覚える必要がある。やってみると、これがけっこう面倒なんだな。また、utf8.pm や encoding.pm は『プラグマ』なので、実行後に制御できないし…。さらに utf8 では、従来の 1バイト文字、2バイト文字という概念が無くなったために『箱組み(ベタ組み)』や『asciiアート』が崩れる…などの問題もある。一般の日本人ユーザーにとっては、一長一短という感じだ。残念ながら Perl5.8 は、jperl と全く同じように使えるわけではない…ということだ。

さて、今頃『日本語処理ルーチン』をリリースするのは、遅すぎるだろうか?


[<<:head #00] [<:prev #01] [here: #02] [>:next #03]

#02 能書き: about "jpway.pl"

jpway とは、実は『 That's the Japanese Way!! 』を略したものだ。この文は foussin分室のスローガンでもある。jpway.pl とは、ぶっちゃけると jcode.pl, Jcode.pm のラッパーだ。文字コードの判定と変換は、それらをエンジンとして処理していく。

つまり、jcode.pl や Jcode.pm の使い方を完全にマスターしている人にとっては jpway.pl は無用の長物ということになる。が、jperl が使えなくったことで Perl プログラミングを諦めてしまったアマチュア・ユーザーにとっては、待望のサブルーチンだろうと思っている。jperl を使っていたユーザーにとっては、世界中の文字コードに対応している Encode の使い方を覚えるより、この jpway.pl の使い方を覚える方が簡単だ。なので、ぜひとも試してほしい。

jpway.pl の存在理由は――

日本語を文字化けせず確実に加工する 『定型ルーチン』を明示する

ということになる。日本語(バイト列の文字列)を加工するためには『一定の手順を踏むこと』が大切で、jpway.pl は日本語処理(文字化け対策)を明確にするための機能を実装している(次のとおり)。

  1. jcode.pl, Jcode.pm どちらでも共通の書式で実行できるラッパーを実装。
  2. 実装しているサブルーチンは、全て『構造化プログラミング』 の、いわゆる『レガシー・スタイル』で記述しているので、jperl を使っていたユーザーにとっては書式が分かり易い。
  3. コード判定、変換精度を高めるための classify 関数(getcode の発展形) を実装。
  4. いったん euc に変換することで、jis, sjis の 0x5c問題の文字化けが回避できる。そのための関数として anyway_euc 関数を実装。
  5. キャラクタ変換には tr/// は使わず、euc_tr 関数(tr///エミュレータ) を使う。euc_tr を実行するには、文字列を事前に euc-jp に変換しておく必要がある。euc_tr 関数も、jcode.pl, Jcode.pm のラッパーに過ぎない。
  6. xeuc (eucモドキ) に変換することで s/// 適用時(文字列置換)の文字化けを回避できる(m// 検索時のミスマッチも回避できる)。そのための関数として euc2xeuc 関数を実装。
  7. 文字列加工が終われば、その文字数が確定する。 対象文字列の文字数を知りたいなら、xeuc文字列を euc に戻し、 read_euc 関数、hankak_ct 関数等を実行する (xeuc を euc に戻すには restore 関数を実行する)。
  8. 全ての処理が済んだら restore 関数を実行し、元の文字コードに戻す、 もしくは任意の文字コードに変換して出力する。

上記のうち、4. 〜 8. は『1行単位のループ処理内』に記述することが可能だ。それを可能にしているのは、classify による『入力文字列の入力経路の明示指定』と、『入力文字コードの種類を分類する機能』のおかげだ。最新版の Perl5.8 では、Encode を使って文字列の判定とエンコード・デコード変換を行っているが、Encode::Guess が抱えている問題を解決する糸口が、この jpway.pl に含まれているかもしれない。


[<<:head #00] [<:prev #02] [here: #03] [>:next #04]

#03 jpway.pl の主要関数

jpway.pl を使うためには、まず最初に次の記述をする必要がある。

require "jpway.pl";
&jpway::init($jname);

上記 2行の記述は必須となる。以下に主要関数の簡単(?)な書式を示す。

&jpway::init($jname);
引数:$jname "Jcode" または "jcode" のどちらかを指定する。 "Jcode" ならば Jcode.pm を使って日本語処理を行う。 "jcode" ならば jcode.pl を使って日本語処理を行う。
($ihandle, $sys_name, $newline) = &jpway::classify($ihandle[, $file]);
概要: &classify は、引数文字列をいくつかのタイプに分類(classify)し、 変換・加工方法を絞り込む。
1. 引数がテキストかバイナリかを判定
2. テキストならば、復改コードを特定
3. テキストならば、文字コードを特定
&classify は、引数の入力経路に着目する。 引数がソースに記述された文字列であるなら、それは間違いなく 『テキスト』と判断できる。同様に、標準入力(STDIN)や、 コマンドライン引数(@ARGV)、DATA ハンドルから読み込まれた引数も『テキスト』と断定できる。 引数がファイルからの入力の場合、そのファイルを約2KB分だけ読み込み、 テキストかバイナリかを判定する。1行ずつではなく、 数行分をまとめて判定することで誤判定が回避される仕組みだ。
このように、入力経路を具体的に指定すれば、 文字コードの判定精度を飛躍的に高めることができる。 &classify では、そこに着目した。
引数:$ihandle 入力経路を表すキーワード(ハンドル名)を指定(次のとおり)。
"FILE" …文字列をファイルから読み込む
"STDIN" …標準入力から読み込むと仮定 (コンソール入力)
"ARGSTR"…コマンドライン引数(@ARGV) から読み込むと仮定(シェルからの入力)
"DATA" …DATA ハンドルから読み込むと仮定
上記以外のキーワードが指定された場合は、 ファイルハンドルと解釈されて "FILE" と同じ扱いとなる。 引数文字列は、英小文字と大文字を区別しない (内部で大文字に変換される)。 ハンドル名として相応しくない文字が含まれている場合、 その文字は取り除かれる。$ihandle が偽値(undef or "")の場合、 デフォルトとして "FILE" がセットされる。このように、 $ihandle の値は適切な置換が行われるため、 その結果を戻値として返す仕様としている。
※上記のほかに "SRC" というキーワードもあるが、 それが必要になるケースはほとんどないと思うので、詳説を省いた。
引数:$file 引数 $ihandle が "FILE" の場合に入力ファイル名を指定する。
戻値: 入力文字列がテキストならば次の戻値を返す。さもなければ undef を返す。
$ihandle → 引数($ihandle)を適切に置換した文字列を返す
$sys_name→ "win", "uni", "mac", "def" を返す(OS名の略称)
$newline → "\x0d\x0a", "\x0a", "\x0d", $/ を返す(復改コード)
戻値は、$ihandle 以外は復改コードに関するものに限定している。 これによって $file が Windowsテキスト、UNIX系テキスト、Mac テキストのどれであるかが特定できる。$ihandle が "STDIN", "DATA", "ARGSTR" の時、復改はシステム・デフォルト($/) がセットされる。 $sys_name が "def" の時は、入出力時に binmode を実行してはいけない(スキップする制御構造が必要)。
備考: &classify は、引数がテキストならば、 その文字コードも特定するが、戻値としては返さず、グローバル・ ハッシュに記録するに留める。ここで得られた文字コードは、 あとでファイルを 1行ずつ処理する際のヒントとして密かに活用される。
($euc_line, $line_code) = &jpway::anyway_euc($line);
概要: とりあえず(anyway)、『入力文字列($line)』 を元の漢字コードから euc-jp に変換する (1行単位のループ処理内に記述できる)。これによって &jpway::euc_tr (tr///エミュレータ)が実行可能になる。
引数:$line 入力文字列(外部から入力された文字列)。
戻値:$euc_line euc-jp に変換後の入力文字列。
戻値:$line_code 変換前の、元の漢字コード。$line_code にセットされる文字列は "sjis", "jis", "euc", "unknown", "utf8", "ascii", undef のどれか ("binary", "ucs2" は undef となる)。
Perl5.8 + Jcode.pm の組み合わせで実行する場合、"unknown" を返す確率は皆無に近い。"ascii" は、jcode.pl で実行しても、 ちゃんと判定する。事前に &classify を実行し、その結果が 『テキスト』ならば、"unknown", undef を返すことは、まずない。
備考: &anyway_euc には、もう1つ別の書式もあるが、 それは後ほど紹介する。
$tr_line = &jpway::euc_tr($line, $from, $to, $opt);
概要: jcode::trans(), $obj->tr() のエイリアス(ラッパー)
$line =~ tr/from/to/opt; のエミュレータ(euc文字の置換が可能)。
引数: $line, $from, $to は euc-jp の文字列で渡さなければならない。
$opt は ascii文字で "c", "d", "cd", "dc" のみをサポート。ただし、 jcode.pl を使って &euc_tr を実行する時は "d" のみをサポートする。サポート対象外の修飾子が指定されても、 ただ単に無視するだけとする。
戻値: $tr_line(変換後の文字列)は euc-jp の文字列で返される。
備考: マッチした文字数は返さない(jcode.pl の仕様に準拠)。 引数の $line は、tr変換前の euc-jp文字列がそのまま保持される。 要するに『リターン・バイ・バリュー』の仕様となっている。
($euc1[, $euc2, ...]) = &jpway::anyway_euc($spc, $str1[, $str2, ...]);
概要: 日本語文字列を『バイト列』として m//, tr///, s/// で検索・加工する場合、まずは euc-jp に変換すると良い。この時、 忘れがちなのが『記述文字列』も変換する必要がある、 ということだ。記述文字列とは、作者が便宜上、そう呼んでいるだけで、 正式名称というわけではないが――
$line =~ tr/from/to/; の from, to
$line =~ m/$pattern/; の $pattern
$line =~ s/$old/$new/; の $old, $new
これら from, to, $pattern, $old, $new のことだ。 これらの文字列は通常、スクリプト上(source code) に直接記述されているので『記述文字列』 と呼ぶことにした。一方の、ファイルなどから読み込まれる $line を 『入力文字列』と呼ぶことにする。この &anyway_euc の書式は、 記述文字列を一括して euc-jp に変換するものだ。もちろん、内部では &[jJ]code::convert() を使って変換している。この書式は、 初期設定のブロックなどに、一度だけ記述すれば良い。
引数:$spc sjis の 0x5c問題を回避するための引数。$spc には通常、 1個の半角スペースを指定する。シングルクォート文字列では、 基本的に 0x5c問題は起こらないのだが、唯一の例外がある。 次の文はエラーとなる→ print '可能';『能』(0x945C)は、2バイト目が 0x5c(\)なので、次の文字(')をエスケープしてしまい、 文字列を閉じることができなくなってエラーとなる。 これを回避する無難な方法は、'可能 ' のように日本語文字列の末尾に 『空白文字』を入れることだ。$spc に空白文字が指定されると、 &anyway_euc は末尾にある余計な空白文字を除去してから 戻値として返す。シングルクォート文字列の中では、 メタ文字を記述できないため、$spc に指定できる特殊文字は、 半角スペースぐらいしかない。$spc は省略できないので、euc, utf8 の場合、空文字列や undef(未定義値)を指定する必要がある。
引数:$str1, str2 記述文字列(スクリプト中に記述された文字列)。
戻値:$euc1, euc2 euc-jp に変換後の記述文字列。
備考: リスト・コンテキスト専用。
【重要】記述文字列を euc に変換するには、 事前に変数に代入する必要がある。
($cc, $xeuc_line[, $xeuc_pat, $xeuc_str, ...]) =
&jpway::euc2xeuc($euc_line[, $euc_pattern, $euc_str, ...]);
概要: euc-jp の文字列は、jis や sjis と比べて文字化けが起こりにくい。 が、それでも s/// による置換で文字化けが発生したり、m// でミスマッチが発生することがある。&euc2xeuc は、 euc-jp の 2バイト文字を一時的に 3バイト文字に変換することで文字化けを回避する。 この 3バイト文字に変換された euc文字を便宜上 xeuc文字(extended euc … eucモドキ) と呼ぶことにする。これによって、m//, s/// による確実な検索・置換が可能になる。
引数: euc-jp に変換済みの文字列を渡さなければならない。
$euc_line (最初の引数)は入力文字列を指定する。
以降の引数はスクリプト中の記述文字列を指定する。
戻値:$cc $cc は euc文字を 3バイト文字にするための材料だ。 引数文字列の中に全く含まれていない未使用の制御文字が $cc として使われ、これを戻値として返す。 $cc は『xeuc → euc → 元の漢字コード』の復元をする際に必要となる。
戻値:$cc 以外 eucモドキに変換された入力文字列($xeuc_line)と、 記述文字列($xeuc_pat, $xeuc_str等)を返す。
$line = &jpway::restore($xeuc_line, $cc[, $line_code]);
概要: eucモドキ(xeuc)を 純正euc に戻し、 euc文字列を元の漢字コードに復元する (または任意の漢字コードに変更する)。
引数:$xeuc_line xeuc(eucモドキ)の文字列を指定する。
引数:$cc $cc は、&euc2xeuc の戻値をそのまま渡す。
引数:$line_code "jis", "sjis", "euc", "utf8" のいずれかを指定する。 元の文字コードに戻すなら、&anyway_euc が返した $line_code をそのまま渡せば良い。
$line_code を指定すると『xeuc→ euc→ $line_code』への復元、
もしくは『euc→ $line_code』への復元をする。
$line_code を省略すると『xeuc → euc』への復元をする。
備考: この関数は、1行単位のループ処理内で実行することを想定しているため、 スカラー・コンテキスト専用となっている。通常、$line_code は、 &anyway_euc が返す戻値をそのまま渡せば、 元の漢字コードに戻る仕組みになっている。$line_code を入力コードとは 別のものに変える場合は、任意の文字コードを指定すると良い。ただし、 jcode.pl を使っている場合、"utf8" への変換はできない。
@euc_line = &jpway::read_euc($euc_line[, $cc]);
概要: euc(xeuc)文字列をシーケンシャルに調べて『文字単位』 のリストにして返す。
引数:$euc_line euc または xeuc に変換済みの文字列を指定(必須)。
引数:$cc xeuc文字列を処理する時は必ず指定。省略すると引数文字列を euc文字列と断定する。$cc は、&euc2xeuc() を実行することで得ることができる。
戻値:@euc_line 文字単位のリスト(文例を参照)。
文例: $euc_str = '私は風desu'; # ← euc文字列
@euc_line = &jpway::read_euc($euc_str);
文例の結果: @euc_line の内容→ ('私', 'は', '風', 'd', 'e', 's', 'u') となる。
備考: 文例の結果を見れば、配列要素の数=文字数 であることは一目瞭然だ。このリストを使えば、全角文字を 1文字として解釈する length は完成したも同然で、全角文字混在の reverse 等も簡単に実現できる。ただし、要素の数=文字数 を実現するため、空文字列を与えると undef を返す。
@count = &jpway::hankak_ct(@euc_line);
概要: 半角文字を1、全角文字を2文字としてカウントする (バイト列の個数ではない)。文字コードが変わると、 同じ文字列でもバイト数が変わる可能性があるので、 安易に length で文字数を求めることはできない。これは、 それを補正する関数として機能する。
引数:@euc_line &read_euc を実行して得た戻値を渡す (euc文字のリスト)。
文例: @count = &jpway::hankak_ct('e', 'u', 'c', '文', '字', '列');
文例の結果: @count の内容は、( 1, 1, 1, 2, 2, 2 ) となる。
考察: 戻値 @count の内容から分かること…
"1" の個数を数えると半角文字の数が分かる … 3(文例の場合)
"2" の個数を数えると全角文字の数が分かる … 3
リスト値の和を求めると半角単位の文字数が分かる … 1+1+1+2+2+2=9
リスト(@count)の個数を数えると文字数が分かる … 6
引数が偽値("" or undef)の場合、文字数 0 ではなく、undef を返す。 これによって『要素の数=文字数』を実現している。
解説: euc では半角カナを2バイトで、補助漢字を3バイトで表現する。 そのため、バイト列の個数と半角単位の文字数が一致しない場合がある。 プレーンテキストを 1行76文字詰めの左右揃えにしたい時などに、 この関数を利用すると良い。

主要関数の数があまりにも多いので、面食らった人もいるかと思う。これだったら、jcode.pl や Jcode.pm だけを使った方が簡単ではないのか? と思った人もいるかもしれない。が、その考えは大間違いだ。jpway.pl に実装している主要関数の多くは、従来まではユーザー自身が記述しなければならなかったものばかりなのだ。つまり、日本語文字列の文字化け対策というのは、それほどまでに大変な処理だった…ということだ。

jpway.pl は、その大変な処理のほとんどを、ちゃんと肩代わりしている…そのことを評価していただきたい。


[<<:head #00] [<:prev #03] [here: #04] [>:next #05]

#04 jpway.pl の使用例(雛型)

例) テキストファイルから 1行ずつ文字列を読み込んで、それを加工する場合

#!/usr/bin/perl

require "jpway.pl";
&jpway::init("Jcode.pm"); # Jcode.pm を使う

# ユーザー設定
$ihandle= "FILE";    # 入力ハンドル名を記述
$ifile  = "foo.txt"; # 入力ファイル名を記述

# classify:入力ファイルの復改コードを特定
# 内部では、ファイル判定(bin or text)と文字コード判定もしている
# ここでの判定結果は、1行単位の文字列処理の際に、密かに反映される
# 戻値が undef ならば、$ifile はテキストではない
($ihandle, $sys_name, $newline) = &jpway::classify($ihandle, $ifile);

# エラートラップ(テキストではない、ならば、実行中止)
# $ifile がバイナリ、特殊ファイル、存在しない、アクセス不可 → 実行中止
$ifile && ($file=$ifile);   $ifile || ($file="Target");
$ihandle || die "Error! $file is not text.\n";

# 記述文字列(source strings)の設定
# 記述文字列 → tr/form/to/,  m/$pattern/,  s/$old/$new/;
# 記述文字列は "sjis", "euc", "utf8" を想定("jis" は想定していない)
# 注意:'日本語 ' → 日本語はシングルクォートで代入。sjis文字列の場合、
#                    文字列の末尾はスペース($spc)にする。末尾の $spc は
#                    &anyway_euc によって自動的に除去される。
# 注意:"\r"      → メタ文字はダブルクォートで代入する。
    $spc =" ";  # ← 半角スペース(sjis の場合。euc, utf8 の場合は空文字列)
    $from="";       $to="";
    $pattern="";    $old="";    $new="";

# &anyway_euc
# 変数に格納した記述文字列(source strings)を、一括して euc に変換する
($from, $to, $pattern, $old, $new) =
 &jpway::anyway_euc($spc, $from, $to, $pattern, $old, $new);

# ファイル入力の時だけ $ihandle を open する
@ihandle = ("STDIN", "ARGSTR", "DATA", "SRC");
foreach (@ihandle) {
    unless ($_ eq $ihandle) {
        open($ihandle, "<$ifile");
        ($sys_name ne "def") && (binmode $ihandle);
        ($sys_name ne "def") && ($/ = $newline);
        $open_flag = 1;
        last;
    }
}

# 出力ファイル
$fh    = "OUT";      # 出力ファイルハンドル名
$ofile = "bar.txt";  # 出力ファイル名を記述
open($fh, ">$ofile") or die;
($sys_name ne "def") && (binmode $fh);

# メイン・ルーチン(1行単位の文字列処理)
while (<$ihandle>) {
    chomp;
  # &anyway_euc(入力文字列を euc に変換)
  # euc に変換すると &jpway::euc_tr が利用できる
    ($euc_line, $line_code) = &jpway::anyway_euc($_);

    # euc_tr(キャラクタ変換)の処理
    $euc_line = &jpway::euc_tr($euc_line, $from, $to);

  # &euc2xeuc…入力文字列、記述文字列を 一括して eucモドキ(xeuc)に変換
  # xeuc にすると、m//, s/// が実行可能となる
  # $cc(制御文字)は、元の漢字コードに復元する時に必要になる
    ($cc, $xeuc_line, $pattern, $old, $new) =
    &jpway::euc2xeuc($euc_line, $pattern, $old, $new);

    # ここに m//, s/// の処理を記述
    $xeuc_line =~ s/$old/$new/;

  # &restore…加工処理が終わったら、xeuc文字列を、元の漢字コードに復元
  # (または任意の漢字コードに変更する)
    $line = &jpway::restore($xeuc_line, $cc, $line_code);
  # 結果を $ofile に出力(原則として \n は使わない)
  # 別の復改で出力したいなら、例えば次のようにする
    #$nl = $jpway::newline{"win"};  print $fh "$line$nl";   # win text
    #$nl = $jpway::newline{"mac"};  print $fh "$line$nl";   # mac text
    #$nl = $jpway::newline{"uni"};  print $fh "$line$nl";   # unix text
    #↑$nl の設定は whileループに入る前にやった方が効率的
    #※出力ファイルハンドルを binmode しないで出力するのは、やめた方が良い
    print $fh "$line$newline"; # $newline は入力文字列の復改
}

($open_flag) && (close $ihandle);
close $fh;
$/ = $jpway::newline{"def"}; # 復改をシステム・デフォルトに戻す

# この後は、ユーザー各自の処理…
__END__

一見すると、かなり冗長に見えるが、コメントを除去すれば結構コンパクトになる。次項のサンプル・スクリプトを見れば、納得してもらえるはず。


[<<:head #00] [<:prev #04] [here: #05] [>:next #06]

#05 サンプル・スクリプト

#!/usr/bin/perl

# "hakogumi.pl" by foussin  (jpway.pl を使ったサンプル・スクリプト)
# ver0.00:2007.12.21 Fri 21:42(初版)
# ver0.10:2007.12.31 Mon 14:36(改訂)

# 日本語処理エンジン:jcode.pl, Jcode.pm どちらでも動作可。両方インストー
# ルされているシステムでは Jcode.pm が優先される(これは jpway.pl の仕様)。

# 概要:ファイルからの入力文字列を指定の桁数で自動折り込みするスクリプト。
#       禁則処理はできない。jis, sjis, euc, utf8 のテキストに対応する。
#       ただし、utf8 に対応するには Jcode.pm が必要となる。

=USAGE "hakogumi.pl"
    書式:perl hakogumi.pl [入力ファイル名 [桁数 [出力ファイル名]]]
    引数($ARGV[0]):テキストファイル名を指定。省略すると標準入力(STDIN)
                    から読み込む。
    引数($ARGV[1]):半角単位で桁数を指定する。省略時のデフォルトは 74。
    引数($ARGV[2]):出力ファイル名を指定する。省略時のデフォルトは、
                    "$ARGV[0].width$ARGV[1].txt" とする。
    備考:標準入力から読み込む場合、":q" を入力すると、実行を終了する。
    注意:桁数は、左端を 0 として数える(通常のエディタとは違う)。
          文字列の中に制御文字が含まれている場合、表示が乱れる可能性が
          あるが、これは仕方がないだろう。そういう仕様とする。
       ※ utf8 のテキストでは、1バイト文字、2バイト文字という概念がなく
          なっているので、指定文字数で自動折り込みしても、右側が綺麗に
          揃わないかもしれない…。
=cut

# ユーザー設定
  $jname = "";    # ←省略すると実行可能な日本語処理エンジンを自動的に特定
  $fh    = "OUT"; # 出力ファイルハンドル名を記述

# 引数チェック
  $ARGV[0] ? ( $ihandle = "FILE" ) : ( $ihandle = "STDIN" );
  $ARGV[0] ? ( $ifile = $ARGV[0] ) : ( $ifile = 'new.txt' );
  $ARGV[1] ? ( $width = $ARGV[1] ) : ( $width = 74 );
  $ARGV[2] ? ( $ofile = $ARGV[2] ) : ( $ofile = "$ifile.width$width.txt" );

require "jpway.pl";
&jpway::init($jname);
($ihandle, $sys_name, $nl) = &jpway::classify($ihandle, $ifile);
$ifile && ($file=$ifile);   $ifile || ($file="Target");
$ihandle || die "error! ... $file is not text.";

@ihandle = ("STDIN", "ARGSTR", "DATA", "SRC");
foreach (@ihandle) {
    unless ($_ eq $ihandle) {
        open($ihandle, "<$ifile");
        ($sys_name ne "def") && (binmode $ihandle);
        ($sys_name ne "def") && ($/ = $nl);
        $open_flag = 1;     last;
    }
}
open($fh, ">$ofile");   ($sys_name ne "def") && (binmode $fh);

# メイン・ルーチン(1行単位の文字列処理)
while (<$ihandle>) {
    chomp;
    ($ihandle eq "STDIN" and $_ eq ":q") && last;
    my $line = $_;
    my ($euc_line, $line_code) = &jpway::anyway_euc($_);
  # $euc_line の文字数が $width 以下なら、無条件で $line を出力
    if ( length($euc_line) <= $width ) {
        print $fh "$line$nl";
    } else {
        my @divide_list = &divide_line($euc_line, $width);
        foreach $j (@divide_list) {
            my $cc;
            $line = &jpway::restore($j, $cc, $line_code);
            print $fh "$line$nl";
        }
    }
}
($open_flag) && (close $ihandle);
close $fh;
$/ = $jpway::newline{"def"};

sub divide_line {
  # 書式:@divide_list = &divide_line($euc_line, $width);
    my ($euc_line, $width) = @_;
    my @euc_line = &jpway::read_euc($euc_line);
    my @count    = &jpway::hankak_ct(@euc_line);
    my $ct = @count;
    my $sum= 0;
    my ($divide_line, @divide_list, $stack);
    for ($j=0; $j<$ct; ++$j) {
        $sum += $count[$j];
        if ($sum <= $width) {   # 文字数が $width に達するまで連結
            $divide_line .= $euc_line[$j];
        } else {                # $width に達した文字列を配列に代入
            @divide_list = (@divide_list, $divide_line);
            $sum = $count[$j];  # カウンタ($sum)をリセット
            $divide_line = $euc_line[$j];
        }
      # loop の最後は $stack に残りの文字列を代入
        ($j==$ct-1) && ($stack = $divide_line);
    }
    @divide_list = (@divide_list, $stack);
    return(@divide_list);
}
__END__

hakogumi.pl の実行例↓

G:\bat>copy con sjis.txt
適当に長い文字列を入力して、とりあえず sjis で保存してみる。
^Z
        1 個のファイルをコピーしました。

G:\bat>hakogumi.pl sjis.txt 12 sjis_12.txt

G:\bat>type sjis_12.txt
適当に長い文
字列を入力し
て、とりあえ
ず sjis で保
存してみる。
# "reverse.pl" ("ARGSTR" のサンプル)
# 2007.12.16 Sun 01:02   by foussin

;# ユーザー設定
    $jname  = "Jcode";      # "Jcode" or "jcode" を指定
    $ihandle= "argstr";     # 入力ハンドル名を記述
    $fh     = "";           # 出力ファイルハンドル名
    $ifile  = "";           # 入力ファイル名
    $ofile  = "";           # 出力ファイル名

require "jpway.pl";
&jpway::init($jname);
($ihandle) = &jpway::classify($ihandle, $ifile);

;# メイン・ルーチン(1行単位の文字列処理)
foreach (@ARGV) {
    ($euc_line, $line_code) = &jpway::anyway_euc($_);
    @euc_line = &jpway::read_euc($euc_line);
    @euc_line = reverse(@euc_line);
    $euc_line = join("", @euc_line); # 逆順にしたリストを連結
  # &restore の書式では $cc は省略できない(この場合、未定義値を指定)
    $line = &jpway::restore($euc_line, $cc, $line_code);
    print "$line\n";
}
__END__

reverse.pl の実行例↓

G:\...\usr\lib>reverse.pl あいうえおabcd
dcbaおえういあ
#!/usr/bin/perl

# "stdin_conv.pl"   2007.12.15 Sat 22:18  by foussin
# コンソールで入力した文字列を『同時通訳的』に別の文字コードに変換して保存

# 書式:perl stdin_conv.pl [変換コード名 [出力ファイル名]]
# 引数:変換コード名  → "jis", "sjis", "euc", "utf8" のいずれかを指定。
#       出力ファイル名→ 追記出力する(上書きではない)。ファイル名を省略
#       すると "$0.$ARGV[0].txt" という名前で出力する。
# 備考:変換コード名を省略すると、jis で出力する。これは &jconv の
#       デフォルト設定だ(jcode::convert の仕様に準拠している)。
# 終了方法:":q" を入力する(これは vi の真似)。

;# ユーザー設定
    $jname  = "Jcode";      # utf8 に対応するため "Jcode" を指定
    $ihandle= "stdin";      # 入力ハンドル名(標準入力専用)
    $fh     = "OUT";        # 出力ファイルハンドル名
    $ocode  = $ARGV[0];     # 出力コード名(変換後の漢字コード)
    $ofile  = $ARGV[1];     # 出力ファイル名
    $ofile || ($ofile = "$0.$ARGV[0].txt");

require "jpway.pl";
&jpway::init($jname);
($ihandle) = &jpway::classify($ihandle, $ifile);

open($fh, ">>$ofile");

;# メイン・ルーチン(1行単位の文字列処理)
while (<$ihandle>) {
    chomp;
    ($_ eq ":q") && last;
    $line = &jpway::jconv($_, $ocode);
    print $fh "$line\n";
}
close $fh;
__END__

stdin_conv.pl の実行例↓

G:\bat>stdin_conv.pl euc euc.txt
コンソールから入力したテキストを、直ちに euc-jp に変換して
保存する。テスト用として、別の文字コードのテキストが必要になった時に
便利だ。
:q

G:\bat>type euc.txt
・ウ・・ス。シ・・ォ、鯣ホマ、キ、ソ・ニ・ュ・ケ・ネ、。「トセ、チ、ヒ euc-jp 、ヒハムエケ、キ、ニ
ハンツク、ケ、・」・ニ・ケ・ネヘム、ネ、キ、ニ。「ハフ、ホハクサ・ウ。シ・ノ、ホ・ニ・ュ・ケ・ネ、ャノャヘラ、ヒ、ハ、テ、ソサ、ヒ
ハリヘ、タ。」

上記は Windows のコマンドプロンプトでの実行例。コマンドプロンプトのデフォルトは sjis なので、euc-jp の文字列は文字化けする。復改コードはプラットフォームのシステム・デフォルトが使われるので、この場合は『crlf』となる。

地味なサンプル・スクリプトばかりに見えると思うが、上記は全て、入力文字列が "sjis", "jis", "euc", "utf8" のどれであっても正常に機能するサンプルになっている。


[<<:head #00] [<:prev #05] [here: #06]

#06 ダウンロード

jpway.pl (最新版)
$VERSION= "1.56";
$UPDATE = "2010.07.07 Wed 23:47(JST)";
$AUTHOR = "pablo foussin(japan)";

ソース本体を見る: (20.1 KB / sjis テキスト)
説明書モドキ(?) を見る: (75.6 KB / sjis テキスト)

ダウンロード: [ ソース本体( jpway.pl )と 説明書モドキ( jpway.pl.commentful.pl )のアーカイブ ]
日本語 Windows 用(sjis/crlf): jpway_1.56_sjis.zip (30.0 KB)
日本語 UNIX 用(eucjp/lf): jpway_1.56_eucjp.zip (29.7 KB)
Linux 用(utf8n/lf): jpway_1.56_utf8n.zip (31.8 KB)
[<:head #00] ... [<<:prev (アマチュア・スタイル!)] ... [<<:home (index.html)] ... [<<:top (top.cgi)]

アマチュア・スタイル! に掲載の文書、プログラムは、 全て pablo foussin の著作物です。
ですが、プログラムについては改変自由のフリーソフトとします。