Perl楽しいから好き

Perlをはじめとしたプログラミング周りのあれこれについて。モダーンなPerlを楽しんでいます。

Perl でUTF-8(BOM無し)ファイルをUTF-8(BOM付き)に変換する方法

Windows 向けのソフトに読み込ませるファイルをUTF-8(BOM付き)にする必要があったので、やってみた。TeraPadで開いて「文字コード指定保存」すればできるんだけど、スクリプト処理の一連の流れに乗せたかったので、やってみますた。

Windows7(64bit)環境でやってま〜す。


#!/usr/bin/perl
# 
# UTF-8(BOM無し) ⇒ UTF-8(BOM付き)にファイルを変換する
#
# 2014.09.07_scripted by perl48

use strict;
use warnings;
use utf8;

# 読み込むファイル名をセット
my $old = 'before.txt';

# 書き出すファイル名をセット
my $new = 'after.txt';

# ファイルハンドルをオープンして、外部テキストファイルの読み込み準備
open my $read, '<', $old
    or die "Couldn't open : $!";

# ファイルハンドルをオープンして、外部テキストファイルへ書き出し準備
open my $write, '>', $new
    or die "Couldn't open : $!";

print {$write} "\x{feff}" ;  # BOMを先頭につける

# 1行ずつ読みで、ただ書き出す
while (my $line = <$read>) {
    chomp $line;
    print {$write} $line . "\n";
}

# ファイルハンドルを閉じる
close $read;
close $write;


File::BOM っていうモジュールもあるみたいだけど、標準の環境で済ませたかったので文頭に{ EF BB BF } をつけてBOMりました。





TeraPadで開くと下のほうのバーに「UTF-8N」ってなってる。これがBOM無し状態。




で、上記のスクリプトを実行すると・・・





TeraPadで開くと下のほうのバーに「UTF-8」ってなる。これがBOM付き状態。






Ubuntu のときは nkf コマンドで済ませちゃいます。コマンドプロンプトPowerShellにも似たような機能あるのかな??




 

Perlで育った僕がJavaを始めた理由。

Perlを使い始めてかれこれ4年ほどになります。僕のプログラミングバージンはPerlに破られたので、変数とか、条件分岐とか、繰り返しとか、配列とかはPerlで身に着けました。ちょこっとモジュールとかも使えるようになったりして、自分の欲しいツールは大体作れるようになりました。


とはいえWEB周りの仕事をしているので、PerlよりはPHPを触る機会の方が多かったりします。ときどき「いつ作ったんだよ?」って思えるくらい古いCGIの修正を依頼されることもあります。そんなレガシーCGIでjcode.pl を見かけたら Encode.pmに載せ替えてみたり。


他にもWindowsのクライアントでの作業自動化のためにC#でアプリ作ったり、作業補助用に作ったPerlスクリプト群をごっそりPythonで書き換えてみたり。メインPCのOSをUbuntuにして積極的にbashを使ってみたり。ローカルでTomcat入れて、Struts触ってみたり。Symfony弄ってみたり。




でも、どの言語使っても結局のところ手続き型言語的な書き方しかできないんですよね。




C#はオブジェクティブですが、ほとんどVisual Studioが勝手にやってくれちゃうし。Perlオブジェクト指向にチャレンジしてみたけど、いまいち馴染めないし。



まー、そんなこんなでオブジェクト指向とちゃんと向き合おうということで、Java始めました。僕は格安レンタルサーバーを主戦場にしているので、「TomcatブっこんでサーバーサイドもJavaでバリバリ」っていうシーンに出くわすことは無さそうです。それでも性的静的言語を、そして本屋のプログラミング書コーナーに行っても一番多く本が置いてあるJavaを身につける価値は絶大なんじゃないかな。



Javaで身につけたことをPerlで生かしていくっていうのも面白いアプローチかなぁ。ま、気長にやっていきます。




Javaを学ぶ本ですが、60件近くある 「スッキリわかるJava入門」のレビュー を読んで、この本を買いました。



今は第2版が出版されてるみたいなので、買うならコッチがいいかも。

 ↓ ↓ ↓


スッキリわかるJava入門 第2版




文中にイラストが多いだけかと思ってましたが、かなりわかりやすく書かれてます。アマゾンレビューの高評価は伊達じゃないですね。




 

WindowsパソコンでPerl置き換え演算子s// で発生した文字化けを Encode.pm でスマートに解決!

マルチバイト文字列が母国語の我々を常に悩ませる問題。

そう、文字化け!


1年ちょい前に書いたエントリーでも文字化けについて書いてるけど、

今日もややハマりそうになって、「perl Encode.pm」でググる

自分のブログに到着。



おかげでスムーズに解決。ありがとう1年ちょい前の自分!

アクセス解析なんか見てると、文字化け系の検索が多いので、

今日の事例を紹介したら少しはお役に立つのでは。



文字列の扱いの基本は

前に書いたとおり

 ______         ________________         ______
|      |       |                |       |      |
| 入力 |======>|  perl の処理   |======>| 出力 | 
|______|       |________________|       |______| 
           入力をdecode      出力前にencode

では、さっそくプラクティス!!


■主な登場ファイル
(1)idle.txt ・・・読み込むファイル(保存文字コード形式SJIS
(2)replace_name.pl ・・・処理するPerlスクリプト(保存文字コード形式UTF-8N)
(3)idle_now.txt ・・・出力するファイル(保存文字コード形式SJIS


■(1)idle.txt の内容

経済が不況になるとアイドルが活躍すると言う評論家がいます。
長引く平成不況に躍進したのはAKB48(*゚∀゚)=3
AKB48(*゚∀゚)=3は紅白歌合戦に出場するなど、
テレビで見ない日はないというくらい激しく活動しています。
EOF


■行いたい処理
文章中の「AKB48(*゚∀゚)=3」を「ももいろクローバーZ(*゚∀゚)モノノフ=3」に書き換えたいっ!!


■(2)replace_name.pl の内容

#!/perl/bin/perl
#
# Scripted by Perl48 2013.03.02

use strict;
use warnings;
use utf8;
use 5.010;    # なんとなくsayを使いたいだけ
use Encode;


# 読み込むファイルと書き込むファイル
my $file_r = './idle.txt';
my $file_w = './idle_now.txt';

# ファイルハンドルをオープン
open my $r, '<', $file_r
    or die "Can't openfile: $file_r: $!";
open my $w, '>', $file_w
    or die "Can't openfile: $file_w: $!";

while (my $line = <$r>) {
    chomp $line;

    # 読み込んだ$line を perl内部文字コードにデコード
    $line = decode("cp932", $line);

    # 「AKB48(*゚∀゚)=3」を「ももいろクローバーZ(*゚∀゚)モノノフ=3」に置き換え
    $line =~ s/AKB48\(\*\゚\゚\)=3/ももいろクローバーZ\(\*\゚\゚\)モノノフ=3/;

    # cp932にエンコードして出力。今っぽくsayしてみる
    #say encode("cp932", $line); # コマンドプロンプトへ表示テスト
    say {$w} encode("cp932", $line);
}

# ファイルハンドルを閉じる
close $w;
close $r;


コマンドプロンプトに表示した結果


Perlで文字化けしちゃったら、Encode.pm について学ぶチャンス!
いつやるか?今でしょ!


Enjoy Encode.pm !

Perl のEncode.pm について。今さらながらに、わかったこと。

特に文字コードを意識することなく

フツーに文字コード shift_jis で書いたスクリプト内で、

日本語を print しても文字化けしないのは、

たまたま処理がうまくいってるだけなんだということが

今さらになって、わかった。

※ちなみに、WindowsXPPerl v5.10.1 な環境でやってます。


たとえば、

#!/usr/bin/perl
# 
# Encode.pm と文字コードについての考察(print_unko_0.pl)
# 2011.10.31_scripted by perl48
# 
# このファイルは文字コード shift_jis で保存してます

use strict;
use warnings;

my $string = 'うんこ';

print $string;


っていうスクリプトでも、問題なく



って出力される。



でも、これは


『まぼろし〜!』(IKKO風に言う)



なぜなら、さっきと同じスクリプト

文字コード UTF-8 で保存するだけで

#!/usr/bin/perl
# 
# Encode.pm と文字コードについての考察(print_unko_1.pl)
# 2011.10.31_scripted by perl48
# 
# このファイルは文字コード utf8 で保存してます

use strict;
use warnings;

my $string = 'うんこ';

print $string;



ガッツリ文字化け〜♪


そこで、Encode.pm の登場〜!!


shift_jis で保存するなら、

#!/usr/bin/perl
# 
# Encode.pm と文字コードについての考察(print_unko_2.pl)
# 2011.10.31_scripted by perl48
# 
# このファイルは文字コード shift_jis で保存してます

use strict;
use warnings;
use Encode;

# 文字コード shift_jis 内のハードコーディングなので、'うんこ'も shift_jis
my $string = 'うんこ';

# perl内部文字コードにデコードして
$string = decode( "shift_jis", $string );

# 出力時にエンコードする ※Windowsのコマンドプロンプトの文字コード shift_jis に合わせる
print encode( "shift_jis", $string );


って書いて、出力は↓





utf8 で保存するなら、

#!/usr/bin/perl
# 
# Encode.pm と文字コードについての考察(print_unko_3.pl)
# 2011.10.31_scripted by perl48
# 
# このファイルは文字コード utf8 で保存してます

use strict;
use warnings;
use Encode;

# 文字コード utf8 内のハードコーディングなので、'うんこ'も utf8
my $string = 'うんこ';

# perl内部文字コードにデコードして
$string = decode( "utf8", $string );

# 出力時にエンコードする ※Windowsのコマンドプロンプトの文字コード cp932 に合わせる
print encode( "cp932", $string );


って書いて、出力は↓



いや〜、Encode.pm は偉大だ!!

Perlで複数行の置き換えをやってみたよ

mをsにしたら結構サクッと置き換えもできちゃった。

てへっ。

#!/usr/bin/perl
# 
# 複数行の置き換えを行うスクリプト-2
# 2011.10.29_scripted by perl48

use strict;
use warnings;

my $multiple_data;

foreach my $data (<DATA>) {
    $multiple_data .= $data
}

#print $multiple_data;

# <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">.<?php  をマッチさせる
# htmlはスラッシュが多いので、s#pattern#pattern2#s という感じでデリミタを # にしてみる
# 改行一文字を . で表すための修飾子 /s をつける
if ($multiple_data =~ s#<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">.<\?php##s ){

    #print $&;
    print $multiple_data;
}

__DATA__
<?
//絵文字
include_once 'include/commons.php';
//ヘッダ情報
include_once 'include/content_type.php';
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<?php include ('include/analytics.php'); ?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<?php include ('include/head.php'); ?>
</head>

Perlで複数行のマッチをやってみたよ

複数行のマッチとかできたら幸せになれるのに・・・

って思いながら結城浩さんの『Perl言語プログラミングレッスン』を読んでたら

できました。

#!/usr/bin/perl
# 
# 複数行の置き換えを行うスクリプト
# 2011.10.29_scripted by perl48

use strict;
use warnings;

my $multiple_data;

foreach my $data (<DATA>) {
    $multiple_data .= $data
}

#print $multiple_data;

# <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">.<?php  をマッチさせる
# htmlはスラッシュが多いので、m#pattern#s という感じでデリミタを # にしてみる
# 改行一文字を . で表すための修飾子 /s をつける
if ($multiple_data =~ m#<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">.<\?php#s ){

    print $&;
}

__DATA__
<?
//絵文字
include_once 'include/commons.php';
//ヘッダ情報
include_once 'include/content_type.php';
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<?php include ('include/analytics.php'); ?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<?php include ('include/head.php'); ?>
</head>

宮川プロダクツWeb::Scraperを使って楽天の商品情報をブッコ抜いてみたよ。

待望の冨田尚樹氏の書籍『Perl CPANモジュールガイド』が発売になった。



早速購入!


「この本の登場で、Perl業界が盛り上がること間違いなし!」


などと妄想しつつ、書籍を参考にしながら以前使いこなせずに挫折した

Web::Scraper を試してみた。


お題は
楽天ショップ別ページから、個別商品URLを取得』

#!/usr/bin/perl
# 宮川プロダクツWeb::Scraperを使って
# 楽天ショップ別ページから、個別商品URLを取得してやんよ
# 2011.04.17_scripted by perl48

use Web::Scraper;
use URI;
use utf8;

# 取得したい部分をXPathで指定する。
# <table class="catchTable">内の1番目の<td>内の<a href="">の@href要素を取得したい。
#
# 【実際のHTMLソース↓】
# <table width="100%" border="0" cellspacing="0" cellpadding="0" class="catchTable"> 
#     <tr> 
#         <td width="3%" valign="top" align="center"> 
#             <a href="http://item.rakuten.co.jp/surugaya-a-too/3511089-1/" target="_top"><img src="http://image.www.rakuten.co.jp/com/img/icon/cir_order.gif" border="0"  alt=""></a> 
#         </td> 
#         <td valign="top" align="left"> 
#             <a href="http://item.rakuten.co.jp/surugaya-a-too/3511089-1/" target="_top"><font size="-1">【中古】生写真/アイドル/AKB48/アイドル生ブロマイド 131 : 大島優子【10p12Apr11】【画】</font></a> 
#             &nbsp;&nbsp;
#             <img src="http://image.www.rakuten.co.jp/com/img/icon/icon_arrow01.gif" width="12" height="12" border="0" alt="" align="absmiddle"> 
#             <a href="http://review.rakuten.co.jp/item/1/239310_12458096/1.1/" target="_top"><font size="-1">感想(1件)</font> 
#             </a> 
#         </td> 
#     </tr> 
# </table>
my $scraper = scraper {
    process '//table[@class="catchTable"]/tr/td[1]/a', 'urls[]' => '@href';
};

# ボットの名前をつけてみる
$scraper->user_agent->agent('perl48bot/0.1'); 

# 取得対象ののURLをセット
my $url = URI->new('http://search.rakuten.co.jp/search/inshop-mall/akb48/-/f.1-p.1-s.1-sf.0-sid.239310-st.A-v.2');
my $res = $scraper->scrape($url);

# 繰り返し変数$i の初期値を定義
my $i = 0;

# デフォルトで1ページ30商品表示のため、30回繰り返す
while( $i<30 ){
    print $res->{urls}->[$i] . "\n";
    $i++;
}


で、実行結果はこちら↓

C:\Documents and Settings\perl48\rakuten>scrape_rakuten.pl
http://item.rakuten.co.jp/surugaya-a-too/1321137-1/
http://item.rakuten.co.jp/surugaya-a-too/3398077-1/
http://item.rakuten.co.jp/surugaya-a-too/3511089-1/
http://item.rakuten.co.jp/surugaya-a-too/3398055-1/
http://item.rakuten.co.jp/surugaya-a-too/3398059-1/
http://item.rakuten.co.jp/surugaya-a-too/1304598-1/
http://item.rakuten.co.jp/surugaya-a-too/3398058-1/
http://item.rakuten.co.jp/surugaya-a-too/3476541-1/
http://item.rakuten.co.jp/surugaya-a-too/3511026-1/
http://item.rakuten.co.jp/surugaya-a-too/1661334-1/
http://item.rakuten.co.jp/surugaya-a-too/2210679-1/
http://item.rakuten.co.jp/surugaya-a-too/3509377-0/
http://item.rakuten.co.jp/surugaya-a-too/3526682-0/
http://item.rakuten.co.jp/surugaya-a-too/3612182-1/
http://item.rakuten.co.jp/surugaya-a-too/3476622-1/
http://item.rakuten.co.jp/surugaya-a-too/1411570-1/
http://sa.item.rakuten.co.jp/surugaya-a-too/a/1666978-1-0417/
http://item.rakuten.co.jp/surugaya-a-too/3511019-1/
http://item.rakuten.co.jp/surugaya-a-too/3464466-1/
http://item.rakuten.co.jp/surugaya-a-too/3511091-1/
http://item.rakuten.co.jp/surugaya-a-too/3526413-1/
http://item.rakuten.co.jp/surugaya-a-too/3476552-1/
http://item.rakuten.co.jp/surugaya-a-too/3631077-1/
http://sa.item.rakuten.co.jp/surugaya-a-too/a/3280493-1-0417/
http://item.rakuten.co.jp/surugaya-a-too/3511098-1/
http://item.rakuten.co.jp/surugaya-a-too/3398065-1/
http://item.rakuten.co.jp/surugaya-a-too/3464494-1/
http://item.rakuten.co.jp/surugaya-a-too/3526669-0/
http://sa.item.rakuten.co.jp/surugaya-a-too/a/1952671-1-0417/
http://item.rakuten.co.jp/surugaya-a-too/3509403-0/


欲しいものは取得できた様子。


取得したいURLの変数部分をインクリメントで変更するか、
取得したいURLの一覧テキストファイルを読み込む形にして
グルグル回せば、ショップ全体の情報を一気にブッコ抜ける。


Perl is cool !