ファイル

Perl で、1GB のデータ(テキストファイル)を作成する方法

次のコードを makefile.pl などというファイル名で保存して、chmod +x ./makefile.pl し、そして ./makefile.pl を実行すれば makefile.pl と同じディレクトリ上に sample.txt に1GiB (ギビバイト=2^30)ぴったりのデータ(「a」の文字で埋め尽くされた テキストファイル)が作成されていると思う。プログラム的には、例えば 1KiB のダミーデータを変数($data)に入れてそれを 1024 x 1024 回書き込んだ方が 1文字ずつ 2^30 回書き込むよりも速いと思うが、試してみたらバァッファオーバーフローしてるのか?うまくいかなかった。 ちなみに、2^30 = 1ギガバイト = 1,073,741,824 バイト = 1024 バイト x 1024 キロバイト x 1024 メガバイト #!/usr/bin/perl open(OUT, '>sample.txt'); for(my $i = 0; $i < 2^30; $i++) { print OUT 'a'; } close(OUT); 1; </code>
続きを読む

PHP で、ディレクトリの中のファイル名を取得する方法

こんな感じ。 !ereg('(^\.$)|(^\.\.$)', $file) とある行は、カレントディレクトリ(.)と、親ディレクトリを除いて処理するためのもの。 $filename[] の配列中に、ファイル一覧が入る。 <?php $dir_path = 'your_directory'; $filename = array(); $dir = dir($dir_path); while($file = $dir->read()) { if(!ereg('(^\.$)|(^\.\.$)', $file)) {
$filename[] = $file;
} } $dir->close(); ?>
続きを読む

Perl で、Base64 のファイルをデコードして元のバイナリファイルに戻す方法

メールに添付されてきた .pdf ファイルが巨大すぎてメールサーバによって分割されて自分のところに配信された結果、Base64 のデータがバラバラに届いたケースを考える。

このような場合は Perl が活躍する。まず、バラバラになった Base64 のファイルは仕方がないのでエディタを使って手作業でくっつけて 1つのファイルにする。そのあと、以下のコマンドを実行すればよい。

使用するモジュール use MIME::Base64; perl -MMIME::Base64 -ne 'print decode_base64($_)' < くっつけたBase64ファイル名.txt > 出力ファイル名.pdf (上は、すべて1行に書く)
続きを読む

Perl で、複数のファイルまたがる文字列を置換するには?

以下のように、replace.conf のハッシュ変数に置換前・置換後の文字列を書いておき、replace.pl を実行する。

replace.conf の内容

#/usr/bin/perl -w use strict; our $HOME_DIR = '/home/youraccount'; our $DIR = "$HOME_DIR/targetdirectory"; our %WORDS = (
            'ブラウザー'   # 置換前の文字列
        =>   'ブラウザ',   # 置換後の文字列

            'FTP'       # 置換前の文字列
        =>     'FTP',    # 置換後の文字列
# ... 以下、
            '置換前の文字列1'
        =>  '置換後の文字列1',

            '置換前の文字列2'
        =>  '置換後の文字列2'
# ... という形で追加していけばよい。
    );
1;

replace.pl の内容

#!/usr/bin/perl require './jcode.pl'; require './replace.conf'; opendir(DIR, $DIR) or die "Couldn't open $DIR directory."; my @files = readdir(DIR); closedir(DIR); foreach my $file (@files) {
if(!(-d $file) && $file ne '.' && $file ne '..' && $file ne __FILE__) {
    print "$file\n";
    replace($file);
}
} sub replace {
my ($file) = @_;

open(IN, "$DIR/$file") or die die "Couldn't open $file.";
my @lines = <IN>;
close(IN);
open(OUT, ">$DIR/$file.tmp") or die die "Couldn't open $file.";
foreach my $line (@lines) {

    &amp;jcode'h2z_sjis(\$line);

    while(my($before, $after) = each %WORDS) {

        if($line =~ /$before/) {
            $line =~ s/$before/$after/g;
        }
    }
    print OUT $line;
}
close(OUT);

rename("$DIR/$file.tmp", "$DIR/$file");
unlink("$DIR/$file.tmp");
} 1;
続きを読む

Perl で、ファイルを読み込んで処理する方法

Perl で、改行で区切られたファイルを読み込んで、1行ずつ処理するには、while() {...} のように書けばよい。

#!/usr/bin/perl # 読み込むファイル名をここに書く our $INPUT_FILE = 'yourfile.txt'; # ファイルをオープンする open(IN, $INPUT_FILE) || die("Can't open a file: $!"); # 上の open で与えられた IN で、 というように書き、while 文中で処理できる while() {
# 1行ずつ表示する
print $_; # 現在処理中の 1行は $_ で表わす。
} # ファイルをクローズする close(IN);

また、 の内容を 1行ずつ細かく処理するには、@IN = ; とするやり方も使える。以下はイテレータのループを while ではなく foreach で処理する例。

#!/usr/bin/perl # 読み込むファイル名をここに書く our $INPU_FILE = 'yourfile.txt'; # ファイルをオープンする open(IN, $INPUT_FILE) || die("Can't open a file: $!"); # 上の open で与えられた IN で、 というように書き、while 文中で処理する @IN = ; # $INPUT_FILE の内容は @IN に入ったので、ここでファイルをクローズできる close(IN); foreach my $line (@IN) {
# 1行ずつ表示する
print $line; # 現在処理中の 1行は $line で表わす。
}
続きを読む

Perl で、Excel のデータ (XML) をアップロードして処理する方法

  1. まず、Excel で 以下のように category1、category2、name、address、phone、link、comment1、comment2 というカラム構成のワークシートを作成し、適当にデータを入力、XML 形式で保存しておく。
    注: 住所は米国の住所でないと正しく処理されないことに注意
  2. 次に、以下にあるように gmap_excel.html と gmap_excel.cgi の 2つのファイルを用意する (.gmap_excel.cgi 中のパスの設定などは適宜変更のこと)。
  3. gmap_excel.html から上の XML ファイルをサーバにアップロードする。
  4. サーバ側では Excel の XML データを処理して、gmap_excel.xml というファイルが出力される。
  5. このサーバ側で出力された gmap_excel.xml ファイルは Google Maps API を使った AJAX クライアントから利用できる。

gmap_excel.html のファイルの内容

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <link rel="stylesheet" href="styles-site.css" type="text/css" />
</head>
<body>
    <form enctype="multipart/form-data" action="gmap_excel.cgi" method="post">
        <input type="hidden" name="MAX_FILE_SIZE" value="2000000" />
        <table width="600">
            <tr><td>Names file:</td><td><input type="file" name="file" />
                    <input type="submit" value="Upload" /></td></tr>
        </table>
    </form>
</body>

gmap_excel.cgi のファイルの内容

#!/usr/bin/perl use lib "path_to_your_perl_lib/lib/perl5/site_perl"; use strict; use Geo::Google; use CGI; our $XML_HEADER = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; # XML ヘッダ our $HOME_DIR = 'your_home_dir'; our $LOCK_DIR = 'path_to_lock_dir/lock'; # do chmod 777 path_to_lock_dir/lock our $LOCK_FILE = 'lock'; our $XML_FILE = 'path_to_your_output_xml/gmap_excel.xml'; # 出力用 XML ファイル名 our $col_index = 0; # Excel シートの現在の列(カラム)インデックス our $row_index = 0; # Excel シートの現在の行インデックス our @data = (); # 出力用データ our @current_data = (); # 現在のデータ # オブジェクト初期化 our $CGI = new CGI; our $GEO = new Geo::Google; # HTML 出力 print qq|Content-type: text/html\n\n|; print qq||; print qq||; print qq|

|; init(); print qq|

|; # 個々のデータのハンドラ sub dataHandler {
my ($expat, $text) = @_;

#  $row_index = 0 と 1 は、Excel で データのヘッダのため必要なし
if($text !~ /^\s+$/ && $row_index > 1) {    #  空白行でなければ
    $current_data[$col_index] = $text;
}
} # 要素の開始 sub startElement {
my ($expat, $tag, %attr) = @_;

if($tag eq 'Row') {            #  <Row>で始まるとき

    $col_index = 0;                #  カラムを初期化
    $row_index++;                #  行を進める
    @current_data = ();            #  現在の行データを初期化
}

if($tag eq 'Data') {            #  <Data>で始まるとき

    $col_index++;                #  列を進める
}
} # 要素の終了 sub endElement {
my ($expat, $tag) = @_;
my ($xml, $html);
my ($lng, $lat, $name, $link, $category1, $category2);
my ($street, $city, $state, $phone, $comment1, $comment2);

#  </Row> のとき
if($tag eq 'Row') {

    push(@data, {    # データに現在の行データを代入

        'category1' => $current_data[1],
        'category2' => $current_data[2],
        'name'      => $current_data[3],
        'address'   => $current_data[4],
        'phone'     => $current_data[5],
        'link'      => $current_data[6],
        'comment1'  => $current_data[7],
        'comment2'  => $current_data[8]
    });
}

#  </Workbook> のとき
if($tag eq 'Workbook') {

    $row_index = -1; #  行数を初期化 ($row_index = 0 はヘッダのため -1 からスタート)
    foreach my $row (@data) {

        $row_index++;

        if($row->{address}) {
            $lng = '';  $lat = '';
            my ($degree) = getDegrees($row->{address});
            if($degree) {
                $lng    = $degree->longitude;
                $lat    = $degree->latitude;
            }
        }

        $name      = $row->{name};
        $link      = $row->{link}
                   =~ /http:\/\/[!#-9A-~]+\.+[a-z0-9]/ ? $row->{link} : '';
        $category1 = $row->{category1};
        $category2 = $row->{category2};
        ($street, $city, $state) = split(',', $row->{address});
        $street    = $street;
        $city      = $city;
        $state     = $state;
        $phone     = $row->{phone};
        $comment1  = $row->{comment1} eq '-' ? '' : $row->{comment1};
        $comment2  = $row->{comment2};

        #  lng と lat が検索できたら (データが入っているということだから)
        if($lng && $lat) {

            $xml    .= "\t<marker\n"
                    .  "\t\tlng       = \"$lng\" lat = \"$lat\"\n"
                    .  "\t\tname      = \"" . $name      . "\"\n"
                    .  "\t\tlink      = \"" . $link      . "\"\n"
                    .  "\t\tcategory1 = \"" . $category1 . "\"\n"
                    .  "\t\tcategory2 = \"" . $category2 . "\"\n"
                    .  "\t\taddress1  = \"$street\"\n"
                    .  "\t\taddress2  = \"$city, $state\"\n"
                    .  "\t\tphone     = \"$phone\"\n"
                    .  "\t\tcomment1  = \"$comment1\"\n"
                    .  "\t\tcomment2  = \"$comment2\"\n"
                    .  "\t/>\n\n";

        } elsif($row_index) {

            $html   .= '<tr><td>'
                    .  "$row_index / $name / $street, $city, $state"
                    .  '</td></tr>';
        }
    }

    $xml    =  $XML_HEADER
            .  "<markers>\n"
            .  $xml
            .  "</markers>\n";

    if($html) {    #  エラーがあれば
        $html    = '<table>'
                 . '<tr><td>***** 以下、緯度経度が検索不能でした。</td></tr>'
                 . $html
                 . '</table>';
    } else {    #  エラーがなければ
        print '<br />done.';
    }

    #  ファイルのオープン
    &file_lock;        #  ファイルロック
    open(XML, ">$HOME_DIR$XML_FILE")
    or die('Cannot open an XML file: ' . "$HOME_DIR$XML_FILE");
    print XML $xml;    #  書き込み
    close(XML);        #  ファイルを閉じる
    &file_unlock;      #  ロック解除

    print $html;
}
} sub getDegrees { # 住所から緯度経度を検索
return $GEO->location( address => shift); # shift = address / リファレンスを返す
} sub init() {
# XMLパーサー作成 ('UTF-8')
my $xml_parser = new XML::Parser(   ProtocolEncoding => 'UTF-8',
                                    Handlers=>{ Start=>\&startElement,
                                                End  =>\&endElement,
                                                Char =>\&dataHandler });
# ファイルのオープンはいらない。input タグで指定した name 属性がそのままファイルハンドルになる
my $xml = $CGI->param('file');

# XMLパース処理
$xml_parser->parse($xml) or    die "XML error: $xml_parser";

# ファイルクローズ
close($xml);
} sub file_lock {
my $wait = 5;
while (!symlink('.',"$HOME_DIR$LOCK_DIR/.$LOCK_FILE")) {
    if (--$wait <= 0) {
# &error( '現在他の方が使用中です。', # 'しばらくしてから再度ご利用ください。');
    }
    sleep (1);
}
} sub file_unlock {
unlink ("$HOME_DIR$LOCK_DIR/.$LOCK_FILE");
} 1;

gmap_excel.xml 出力例

<?xml version="1.0" encoding="UTF-8"?>
<marker
    lng       = "-122.125690" lat = "37.413570"
    name      = "Tofu House"
    link      = ""
    category1 = "Restaurant"
    category2 = "Korean"
    address1  = "4127 El Camino Real"
    address2  = " Palo Alto,  CA 94306"
    phone     = "650-424-8805"
    comment1  = ""
    comment2  = "Santa Clara の Tofu House と同じ店。"
/>
...
続きを読む

Perl で、アップロードしたファイルを処理する

Perl の場合は、CGI として動作させる場合は、必ず


print qq|Content-type: text/html\n\n|;

という行が必要な以外は、PHP とほとんど同じである。 アップロードには、いつも使っているおなじみの CGI モジュールを使う。


use CGI;

そうすると、以下のようにアクセスできる。


our $CGI = new CGI;
...
my $xml = $CGI->param('file');

$xml はファイルポインタが入っている。通常は open(IN, "..."); などするときの IN にあたるものである。以下は、コード。アップロードする側の HTML ファイルは PHP でアップロードファイルを処理するで紹介したのと同じ。

#!/usr/bin/perl use strict; use CGI; our $CGI = new CGI; print qq|Content-type: text/html\n\n|; init(); sub init() {
# XMLパーサー作成 ('UTF-8')
my $xml_parser = new XML::Parser(    ProtocolEncoding => 'UTF-8',
                                    Handlers=>{    Start=>\&startElement,
                                                End  =>\&endElement,
                                                Char =>\&dataHandler    });
# ファイルのオープンはいらない。input タグで指定した name 属性がそのままファイルハンドルになる
my $xml = $CGI->param('file');

# XMLパース処理
$xml_parser->parse($xml) or    die "XML error: $xml_parser";

# ファイルクローズ
close($xml);
} 1;
続きを読む

Perl で、ファイルをロック・アンロックする方法

ファイルのロックアンロックは、flock 関数を使う方法もあるが、この方法はすべてのプラットフォームで使えるというわけではない。 そのため、OS のシンボリックリンク機能を使う。OS がファイルを作成するときに排他制御を行うのを利用する。空のファイルを作成し、そのファイルが存在するかどうかチェックする方法と一見変わらないように見えるが、この方法だとファイルをオープンし、クローズするという2ステップが必要となる。このため、完全な排他にはならない。よって、シンボリックリンクというところがミソである。シンボリックリンクの他には、ディレクトリを作成する方法、ファイル名を変更する方法もあるが、私が知る限りはこの方法を紹介しているサイトが多いように思う。

使用するモジュール


なし
#!/usr/bin/perl our $HOME_DIR = 'ホームディレクトリをここに書く'; our $LOCK_DIR = "$HOME_DIR/lock"; # このディレクトリのアクセス権は 777 である。 our $LOCK_FILE = 'ロックするファイル名をここに書く。なんでもよい。'; sub file_lock { my $wait = 5; while (!symlink(".","$HOME_DIR/$LOCK_DIR/.$LOCK_FILE")) {
   if (--$wait <= 0) {
       # &error('現在他の方が使用中です。しばらくしてから再度ご利用ください。');
   }
   sleep (1);
} } sub file_unlock { unlink ("$HOME_DIR/$LOCK_DIR/lock/.$LOCK_FILE"); } 1;
続きを読む
ファイル を購読