ishiducaの日記 このページをアンテナに追加 RSSフィード

2009-03-12

[][]decodeした場合とencodeした場合のlength関数の違い 14:26 decodeした場合とencodeした場合のlength関数の違い - ishiducaの日記 を含むブックマーク はてなブックマーク - decodeした場合とencodeした場合のlength関数の違い - ishiducaの日記 decodeした場合とencodeした場合のlength関数の違い - ishiducaの日記 のブックマークコメント

メモ

  • decodeした場合は、文字数でカウントする
  • encodeした場合は、オクテット(バイト?)でカウントする

「まるごとPerl」に載ってた。

以上 ...だけではなんなので

こんなので試してみた

$ perl -MEncode -wl -e 'print length decode("utf-8",$_) foreach( ("アイウエオ","アイウエオ","abcde") );'
# 結果
5
5
5

上はdecodeした場合(僕の環境だとデフォルトで utf-8 なので)、全部 5、つまり文字数で数えていることが分かります。次はencodeした場合

$ perl -MEncode -wl -e 'print length encode("utf-8",decode("utf-8",$_)) foreach( ("アイウエオ","アイウエオ","abcde") );'
15
15
5

utf-8 でエンコードした文字だと日本語5文字で15オクテットなので、1文字は 3オクテットということがわかります。これを EUC-JP で試してみる。

$ perl -MEncode -wl -e 'print length encode("EUC-JP",decode("utf-8",$_)) foreach( ("アイウエオ","アイウエオ","abcde") );'
10
10
5

EUC-JPだと日本語は 2オクテット。



追記 2009.03.15

foreachループの代わりに、mapでもいい(追記する必要はないんだけど、mapを使ってみたかったので追記する)

$ perl -MEncode -wl -e '$,="\n";print map{ length decode("utf-8",$_) }("アイウエオ","アイウエオ","abcde");'
トラックバック - http://perl.g.hatena.ne.jp/ishiduca/20090312

2009-03-11

[][][]日本語の文書を適当に整形する 15:46 日本語の文書を適当に整形する - ishiducaの日記 を含むブックマーク はてなブックマーク - 日本語の文書を適当に整形する - ishiducaの日記 日本語の文書を適当に整形する - ishiducaの日記 のブックマークコメント

長い日本語の文章を適当な位置で改行したり、適当にインデントを付けたい場合に使いたいスクリプトが(あるのかもしれないけど)見当たらないので、いい加減に作ってみた。メールで文書を貼付けるとかに使う。

こんな原文なのを

安全管理体制
    次に掲げる者を選任し、輸送の安全確保について責任及び権限を定めて、輸送の安全確保のための体制を確立(「組織図」参照)し、社内への周知を徹底する
    -安全総括管理者
        取締役のうち、貨物自動車運送事業輸送安全規則第2条の6に規程する要件を満たす者の中から、安全統括管理者を選任する。安全統括管理者は次の責務を有する。
        -全社員に対し、関係法令等の遵守と輸送の安全確保が最も重要であるという意識を徹底すること
        -輸送の安全の確保に関し、その実施及び管理の態勢として、安全マネジメントシステムを確立し、実施し、維持すること

こうして、

$ perl formed 規程文書.txt

こんな感じに整形する

安全管理体制
    次に掲げる者を選任し、輸送の安全確保について責任及び権限を定めて、輸送
    の安全確保のための体制を確立(「組織図」参照)し、社内への周知を徹底す
    る
    - 安全総括管理者
        取締役のうち、貨物自動車運送事業輸送安全規則第2条の6に規程する要
        件を満たす者の中から、安全統括管理者を選任する。安全統括管理者は次
        の責務を有する。
        - 全社員に対し、関係法令等の遵守と輸送の安全確保が最も重要であると
          いう意識を徹底すること
        - 輸送の安全の確保に関し、その実施及び管理の態勢として、安全マネジ
          メントシステムを確立し、実施し、維持すること

この辺とかどうにかしたい

  • 半角の日本語(半角の片仮名とか)使うと崩れる
  • utf-8だけなの
  • 禁則処理しない

formedスクリプトのコード

#!/usr/bin/perl -s -wnl
# Usage: $ perl formed [-len=length] file [file ...]
use strict;
use Encode;
our ($len);
BEGIN {
    $len or $len = 36;
    $len = $len * 2;
}
my $indent   = '';
my $list_flg = '';
my $new_str = '';
my $width   = 0;
$_ = decode('utf-8',$_);
s/^\t/    / and s/\t/    /g;
s/^ +-/$& /g;
/^( +)(-?)/g and
  $indent = $1 and
    $2 and $list_flg = "  ";

while( length > 0 ){
    my $char = substr($_,0,1);
    $_       = substr($_,1);
    $char = encode('utf-8',$char);
    my $char_bytes =
      length $char == 3 ? 2 : 1;
    $new_str .= $char;
    $width += $char_bytes;
    if( $width > $len ){
        print $new_str;
        $new_str = '';
        $width = 0;
        $_ = $indent . $list_flg . $_;
    }
}
print $new_str;

変換の効率めちゃくちゃ悪いです...orz

CressCress2009/03/11 21:29decodeとencodeの引数に 'utf-8' が決め打ちになっているのを $len のように引数から取ってやったらどうでしょうか。Encode::Guess という自動判別モジュールもありますが、あまり精度がよくないです。
半角カナだと妙なことになるのは…… /[ア-ン]/ みたいな正規表現で分けてやるか、あるいはいっそ全角にしてしまうとか……なんかバイト数気にしているみたいですが、全角にしてやればsubstrで一発です。

ishiducaishiduca2009/03/12 14:22> 半角カナだと妙なことになるのは…… /[ア-ン]/ みたいな正規表現で分けてやるか、
> あるいはいっそ全角にしてしまうとか

業務でも使っているので、全角に変換するのはさけたいところなので正規表現でチェックするが現実的かも。

トラックバック - http://perl.g.hatena.ne.jp/ishiduca/20090311

2008-11-04

[][]価格.comの検索結果をJSON形式で表示するスクリプトを作ってみる 15:51 価格.comの検索結果をJSON形式で表示するスクリプトを作ってみる - ishiducaの日記 を含むブックマーク はてなブックマーク - 価格.comの検索結果をJSON形式で表示するスクリプトを作ってみる - ishiducaの日記 価格.comの検索結果をJSON形式で表示するスクリプトを作ってみる - ishiducaの日記 のブックマークコメント

文字コード変換の実践として、価格.comの検索結果を抜き出してJSON形式で出力するスクリプトをつくってみる。

概要は、こんな感じ。

  • スクリプト名は、仮に kakakudot.pl。
  • ターミナルから利用する場合 > perl kakakudot.pl 検索語彙
  • ブラウザから利用する場合 サーバー上のアドレス?検索語彙 例:http://ishiduca.jp/kakakudot.pl?竹林と美女
  • 上記のような方法でリクエストを送ると、JSON形式で商品リストが返ってくる。
  • http://kakaku.com/book_cd_dvd/ 価格.com - 本・CD・DVD の検索結果を利用する。
  • 20件以上は表示されないので、実用には向かない。

技術的な点で必要なこととか

  • 価格.com の検索では Shift_JIS でリクエストを送る必要があるので、utf-8 のリクエストを一度 Shift_JIS に変換して、価格.com に送る。
  • 返ってきた結果は Shift_JISHTML なので、必要な部分だけ正規表現で抜き出す。
  • 文字コードを utf-8 に直す。JSON形式にして出力する

kakakudot.pl

#!/usr/bin/perl

use strict;
use LWP::Simple;
use Encode;
use CGI;

my $uri = 'http://search.kakaku.com/ksearch/search.aspx'
    . '?category=0010&search.x=23&search.y=9&query=';

my $query = undef;
if( $ENV{'QUERY_STRING'} ){
        $query = $ENV{'QUERY_STRING'};
        $query =~ tr/+/ /;
        $query =~ s/%([a-fA-F0-9]{2})/pack('C', hex($1))/eg;
} else {
        $query = $ARGV[0];
}

my $enc_sjis = find_encoding('Shift_JIS');
my $enc_utf8 = find_encoding('utf-8');

$uri .= $enc_sjis->encode($enc_utf8->decode($query));
my $html = get($uri);

# 検索結果のHTMLから、解析するための正規表現
(my $Reg =<<'REG') =~ tr/\n//d;
<div class="leftBox">
.*?<img src="(.*?)".*?</div><div class="rightBox">
.*?URL=(.*?)".*?>(.*?)</A>
.*?<span class="price">(.*?)</span>
.*?<p class="details">(.*?)</p>
.*?<span class="storeName">(.*?)</span>
REG
;

# ここから表示の部分
print CGI::header('text/plain; charset=utf-8') if $ENV{'QUERY_STRING'};
my $c = 0;
print "[\n";
while( $html =~ m/$Reg/gs ){
        my $str = undef;
        $str .= ",\n" if $c > 0;
        $str .= "{\n"
            . "\t\"imgURL\"      : \"$1\",\n"
            . "\t\"htmlURL\"     : \"$2\",\n"
            . "\t\"title\"       : \"$3\",\n"
            . "\t\"price\"       : \"$4\",\n"
            . "\t\"description\" : \"" . &notag($5) . "\",\n"
            . "\t\"webshop\"     : \"$6\"\n}";
        print $enc_utf8->encode($enc_sjis->decode($str));
        $c++;
}
print "\n]";

sub notag{
        local $_ = shift;
        $_ =~ s/<.*?>//g;
        return $_;
}

実行してみる

> perl kakakudot.pl きつねのはなし

結果

[
{
	"imgURL"      : "http://ecx.images-amazon.com/images/I/41R2APX4E3L._SL160_.jpg",
	"htmlURL"     : "http://www.amazon.co.jp/dp/4104645028/?tag=kakakucom-ss-22&creative=380337&creativeASIN=4104645028&linkCode=asn",
	"title"       : "きつねのはなし",
	"price"       : "&yen;1,470",
	"description" : "著者&#58;森見登美彦ISBN&#58;9784104645022",
	"webshop"     : "Amazon"
},
{
	"imgURL"      : "http://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/1046/10464502.jpg?_ex=128x128",
	"htmlURL"     : "http://hb.afl.rakuten.co.jp/hgc/040be769.42689f8e.040be76a.9510edc2/?pc=http%3A%2F%2Fitem.rakuten.co.jp%2Fbook%2F4143405%2F",
	"title"       : "きつねのはなし",
	"price"       : "&yen;1,470",
	"description" : "著者&#58;森見登美彦出版社&#58;新潮社サイズ&#58;単行本ページ数&#58;268p発行年月&#58;2006年10月この著者の新着メールを登録する今週の著者インタビューは森見登美彦さん。第15回日本ファンタジーノベル大賞受賞作『太陽の塔』でデビューした森見さんが...",
	"webshop"     : "楽天ブックス - 楽天市場"
},
{
	"imgURL"      : "http://a248.e.akamai.net/f/248/37952/7d/image.shopping.yahoo.co.jp/i/g/7andy_31794025",
	"htmlURL"     : "http://rd.store.yahoo.co.jp/7andy/31794025.html",
	"title"       : "きつねのはなし  /森見登美彦/著 [本]",
	"price"       : "&yen;1,470",
	"description" : "【セブン-イレブンで24時間受取りOK・送料0円!】著者/訳者名&#58;森見登美彦/著出版社名&#58;新潮社発行年月&#58;2006年10月関連キーワード&#58;キツネノハナシきつねのはなし、モリミ,トミヒコもりみ,とみひこ、シンチヨ...",
	"webshop"     : "セブンアンドワイ ヤフー店 - Yahoo!ショッピング"
},
{
	"imgURL"      : "http://image.books.livedoor.com/upload/image01/4/1/3001041-1.jpg",
	"htmlURL"     : "http://ck.jp.ap.valuecommerce.com/servlet/referral?sid=2327384&pid=875565628&vc_url=http://books.livedoor.com/item4104645028.html",
	"title"       : "きつねのはなし",
	"price"       : "&yen;1,470",
	"description" : "細長く薄気味悪い座敷に棲む狐面の男。闇と夜の狭間のような仄暗い空間で囁かれた奇妙な取引とは。『太陽の塔』の著者が紡ぐ、京の骨董屋をめぐる奇譚集。",
	"webshop"     : "livedoorブックス"
{
]

成功したっぽい。

実際には、これだけじゃ面白くも何ともないので、二次利用してこそ面白いんだと思います。あと、JSONじゃなくてRSS形式とかYAML形式でもいいんですが、それはまた。

[][][]Encode::find_encodingを使って文字コードを変換する 11:01 Encode::find_encodingを使って文字コードを変換する - ishiducaの日記 を含むブックマーク はてなブックマーク - Encode::find_encodingを使って文字コードを変換する - ishiducaの日記 Encode::find_encodingを使って文字コードを変換する - ishiducaの日記 のブックマークコメント

前のエントリー:http://perl.g.hatena.ne.jp/ishiduca/20081102/1225609360 :内部処理しないで文字コードを変換する]を書いた後に、otsune さんから find_encoding を使ったほうが速いですよ。と教えていただいたので使ってみました。

が、その前に、間違いがあったので訂正します。

 × # utf-8 から Shift_JISに変換

 ○ # Shift_JIS から utf-8 に変換

さて、前回と同じ事を find_encoding(文字コード) を使ってやってみる。

>perl
use strict;
use LWP::Simple;
use Encode;

my $uri = 'http://www.digitalside.net/bbs2/blog.html';
my $html = decode('Shift_JIS',get($uri));

my $enc_object = find_encoding("utf-8");
print $enc_object->encode($html);

成功。

ただ、どういう仕組みかよくわからないので、ググってみたら 弾さんが書いてました。引用するのが面倒なので、http://blog.livedoor.jp/dankogai/archives/50815457.html 404 Blog Not Found:perl tips - Encodeを速く使う方法 を参照してください...だけでは、ぼんやりしてしまうのでちょっとだけ。

  • Encode::find_encoding(文字コード) は文字コード変換用の器(オブジェクト)を生成する
  • decodeメソッドで任意の文字列のデコードを行う
  • encodeメソッドで任意の文字列のエンコードを行う

...まちがってたらすみません。

最後の2行について

my $enc_object = find_encoding("utf-8"); # 文字コード変換用(ただし utf-8 のみ)を生成
print $enc_object->encode($html);        # $html を utf-8 でエンコードして出力

print encode("utf-8", $html);

と同じことをやってると考えていいのかな、と思います。

さて、次に失敗例

> perl
use strict;
use LWP::Simple;
use Encode;

my $uri = 'http://www.digitalside.net/bbs2/blog.html';
my $html = get($uri);

my $enc_object = find_encoding("utf-8");
print $enc_object->encode($html);

当たり前の話ですが、デコードしていない(Shift_JIS から Unicode文字列 に直していない)ので、当然文字化けします。

さて、最初の例で my $html = decode('Shift_JIS',get($uri)); を使っているんですが、せっかくなので decode(文字コード, $str) の代わりに find_encoding を使ったやり方も書いてみます。

> perl
use strict;
use LWP::Simple;
use Encode;

my $uri = 'http://www.digitalside.net/bbs2/blog.html';
my $html = get($uri);

my $enc_sjis = find_encoding('Shift_JIS');
my $enc_utf8 = find_encoding('utf-8');
print $enc_utf8->encode($enc_sjis->decode($html));

成功。

otsune さん、ありがとうございました。

otsuneotsune2008/11/04 13:15さらに
encode("utf-8", $html)

encode_utf8($html)
のほうが。

ishiducaishiduca2008/11/04 16:06ありがとうございます><

ishiducaishiduca2008/11/04 20:01JSON形式で出力したデータは、こんな感じで利用する。
http://ishiduca.sakura.ne.jp/doc/kakakudot.html

別に新しい事は何もないけど、まあ。

SialanSialan2012/11/01 21:21This is the percfet way to break down this information.

ytyoqgcytyoqgc2012/11/02 10:31WwhIl8 <a href="http://gaodiuhpidxj.com/">gaodiuhpidxj</a>

hwhjkkegaplhwhjkkegapl2012/11/02 15:03INGVWA , [url=http://jkhcdhoytizz.com/]jkhcdhoytizz[/url], [link=http://bjqoywtvimij.com/]bjqoywtvimij[/link], http://ilbzqbfywozx.com/

swkohnswkohn2012/11/04 23:42IaCvAU <a href="http://ryxbhqjtcdzd.com/">ryxbhqjtcdzd</a>

yhrprweyhrprwe2012/11/05 12:57XbVWkx , [url=http://jvseelphdmet.com/]jvseelphdmet[/url], [link=http://lvnpyyjiggzm.com/]lvnpyyjiggzm[/link], http://fhxoysrrjagh.com/

トラックバック - http://perl.g.hatena.ne.jp/ishiduca/20081104

2008-11-02

[][]内部処理しないで文字コードを変換する 16:02 内部処理しないで文字コードを変換する - ishiducaの日記 を含むブックマーク はてなブックマーク - 内部処理しないで文字コードを変換する - ishiducaの日記 内部処理しないで文字コードを変換する - ishiducaの日記 のブックマークコメント

内部処理しないで文字コードを変換するなら Encodeモジュールの Encode::from_to($str, '処理前の文字コード' => '処理後の文字コード') を使った方が楽

ためしに「DigitalSide ザ・ステレオ屋」の更新ページは 'Shift_JIS' なので、これを 'utf-8' に直してみる。

参考:http://www.digitalside.net/bbs2/blog.html: DigitalSide ザ・ステレオ屋 AudioBlog]

> perl
use strict;
use LWP::Simple;
use Encode;

my $uri = 'http://www.digitalside.net/bbs2/blog.html';
my $html = get($uri);

# utf-8 から Shift_JISに変換
Encode::from_to($html, 'Shift_JIS' => 'utf-8');

print $html;

成功。

otsuneotsune2008/11/03 13:30from_toは重いので
my $enc_object = find_encoding("Shift_JIS");
でOOなEncodeオブジェクトを作っておいて

print $enc_object->encode($html);

でencodeする手法を覚えちゃうのがいいかも。

ishiducaishiduca2008/11/03 21:36>> otsune さん
ありがとうございます。
早速試してみたいと思います。記事の
> # utf-8 から Shift_JIS に変換

> # Shift_JIS から utf-8 に変換
の間違いでしたので、
my $enc_object = find_encoding("utf-8");
print $enc_object($html);
なんですね。

BaieBaie2014/05/07 08:31I'm relaly into it, thanks for this great stuff!

トラックバック - http://perl.g.hatena.ne.jp/ishiduca/20081102

2008-10-29

[][][]EUC-JPの文字列のファイルを読み込んで、処理をしてUTF-8で出力する その1 失敗 16:47 EUC-JPの文字列のファイルを読み込んで、処理をしてUTF-8で出力する その1 失敗 - ishiducaの日記 を含むブックマーク はてなブックマーク - EUC-JPの文字列のファイルを読み込んで、処理をしてUTF-8で出力する その1 失敗 - ishiducaの日記 EUC-JPの文字列のファイルを読み込んで、処理をしてUTF-8で出力する その1 失敗 - ishiducaの日記 のブックマークコメント

  • euc-jpでエンコードした任意のテキストファイル 'test.txt'
  • perlスクリプトは 'utf-8' で書いたものと 'euc-jp' で書いたもの 二つ用意する
  • 'use utf8' で嵌る点を探ってみる

まず、'test.txt' の中身

日本語

perlのコード

#!/usr/bin/perl

use strict;
use warnings;
use utf8;
use Encode;

my $log = 'test.txt';

open( my $fh, "<", $log ) or die("can not open $log: $!");
my $line = <$fh>;
close( $fh );

$line = decode('euc-jp', $line);
$line =~ s/語/人/;
print encode('utf-8', $line);

'utf-8' で保存したもので実行すると

> perl encodetest.pl

# [結果]
> 日本人

成功。つづいて 'euc-jp' で保存したもので実行すると

> perl encodetest.pl

# [結果]
Malformed UTF-8 character (unexpected continuation byte 0xb8, with no preceding start byte) at enctest.pl line 15.
Malformed UTF-8 character (1 byte, need 3, after start byte 0xec) at enctest.pl line 15.
Malformed UTF-8 character (unexpected continuation byte 0xbf, with no preceding start byte) at enctest.pl line 15.
Malformed UTF-8 character (1 byte, need 2, after start byte 0xcd) at enctest.pl line 15.
日本語

失敗。'use utf-8' は 'UTF-8'で書かれたコードでないと駄目っぽい。


参考

なので、'use utf8' の代わりに 'use encoding' を使う

EUC-JPで保存された場合は use encoding 'euc-jp', STDOUT => 'utf-8' を使う

#!/usr/bin/perl

use strict;
use warnings;
use encoding 'euc-jp', STDOUT => 'utf-8';
# EUC-JP で書かれたコードの内部で扱う文字リテラルを Unicode文字列 に扱い
# 標準出力時に UTF-8 で出力する
# $_ = decode('euc-jp', $_);
# print encode('utf-8', $_);
# みたいなもんか
use Encode;

my $log = 'test.txt';

open( my $fh, "<", $log ) or die("can not open $log: $!");
my $line = <$fh>;
close( $fh );

$line = decode('euc-jp', $line);
$line =~ s/語/人/;
print $line;
> perl encodetest.pl

# [結果]
> 日本人

ついでに...

use encoding 'euc-jp'; として STDOUT => 'utf-8'; を削って print $line; を print encode('utf-8', $line); とすると、エラーがかえってくる

"\x{0097}" does not map to euc-jp at enctest.pl line 16.
"\x{00a5}" does not map to euc-jp at enctest.pl line 16.
"\x{009c}" does not map to euc-jp at enctest.pl line 16.
???\x{0097}\x{00a5}???\x{009c}?̏????돢?

同じような例は、↓にあった

[][][]UTF-8の文字列を処理してUTF-8で出力する その1 失敗 14:32 UTF-8の文字列を処理してUTF-8で出力する その1 失敗 - ishiducaの日記 を含むブックマーク はてなブックマーク - UTF-8の文字列を処理してUTF-8で出力する その1 失敗 - ishiducaの日記 UTF-8の文字列を処理してUTF-8で出力する その1 失敗 - ishiducaの日記 のブックマークコメント

  • macbookのターミナルはデフォルトでUTF-8に設定したもの(つまり、このコードはUTF-8
  • 'にほんご' を 'えいご' に変換して、出力する(つもり)
> perl
use strict;
use warnings;
use utf8;
# 以下は UTF-8の文字リテラルとして扱うようになる
use Encode;

my $str = 'にほんご';
$str =~ s/にほん/えい/;

# このままだと 'Wide character in print at - line' とおこられるのでエンコードする
print encode('utf-8', $str);

# [結果]
> えいご

これは成功。次、失敗例

> perl
use strict;
use warnings;
use Encode;

my $str = 'にほんご';
my $uni = decode('utf-8', $str);
$uni =~ s/にほん/えい/; # ここがまずい
print encode('utf-8', $uni);

# [結果]
> にほんご

んで、こうした

> perl
use strict;
use warnings;
use Encode;

my $str = 'にほんご';
my $uni = decode('utf-8', $str);
my $org = decode('utf-8', 'にほん'); # マッチングテストする文字列もデコード
my $rep = decode('utf-8', 'えい');   # 同上
$uni =~ s/$org/$rep/;
print encode('utf-8', $uni);

# [結果]
> えいご

成功。結構嵌った

tokuhiromtokuhirom2008/10/29 17:26use encoding 'euc-jp';

LuiizLuiiz2012/08/20 09:17I'm so glad I found my sotluion online.

zxlvfibzxlvfib2012/08/20 19:09urWEMK <a href="http://jjvttpbkwbxf.com/">jjvttpbkwbxf</a>

duhckkvduhckkv2012/08/24 22:0491ON07 , [url=http://zsixfgrcjonw.com/]zsixfgrcjonw[/url], [link=http://izhprwlalzjv.com/]izhprwlalzjv[/link], http://nzdgjulpapwp.com/

トラックバック - http://perl.g.hatena.ne.jp/ishiduca/20081029