燈明ブログ

現状は小池啓仁の応援ブログ

Perlで文字列長(バイト数)を求めるにはパート2

約2年前の2007/12/3に以下の記事を書きました。

しかし、length関数は、utf8 pragmaするとバイト数でなく、文字数を返すようになるのです。
で、バイト数を求めるにはと…ググると以下のfbisさんのページに遭遇しました!

このページでは、学ぶべきことが沢山あったのでメモしときます。

◆引用ソース(多少アレンジさせていただきました)

use strict;
use warnings;

# ポイント1
use utf8;

my $utf8_str = 'シンガー小池啓仁';
my $bytes;

# ポイント2
$bytes = length $utf8_str;
print $bytes, "\n";

# ポイント3
#require bytes;
use bytes();

# ポイント4
$bytes = bytes::length $utf8_str;
print $bytes, "\n";

# ポイント5
$bytes = length unpack('a*',$utf8_str);
print $bytes, "\n";

# ポイント6
{
    use bytes;
    $bytes = length $utf8_str;
    print $bytes, "\n";
}

# ポイント7
use Encode;
Encode::_utf8_off($utf8_str);
$bytes = length $utf8_str;
print $bytes, "\n";
Encode::_utf8_on($utf8_str);
http://d.hatena.ne.jp/fbis/20080126/1201320719

◆実行結果

C:\perltest>length.pl
8
24
24
24
24

『シンガー小池啓仁』の文字列数は8で、バイト数は24になります。
UTF-8では、漢字1文字を3バイトでエンコーディングします。

◆ポイント1

use utf8;

utf8 pragmaの宣言です。
これは、大雑把にいってしまうと、文字列をちゃんと文字列として扱います。
utf8 pragma以前は、文字列をバイト列として扱っていた為に、文字化け等の問題がありました。
今のPerlで漢字を扱う場合は、utf8 pragmaを宣言し、ソースの文字コードUTF-8にするのが推奨です。

◆ポイント2

$bytes = length $utf8_str;

これは、標準の組み込み関数(メインモジュール)のlengthを使用していますが、utf8 pragmaの時は、バイト数でなく文字数を返します。

◆ポイント3

#require bytes;
use bytes();

require bytesとuse bytes()は、ほぼイコールです。
require bytesは、実行時にbytesモジュールを読み込みます。
use bytes()は、コンパイル時にbytesモジュールを読み込み、そして、空リスト()を指定しているので、このモジュールからのインポートは無しです。
もし、空リストを指定しないと(use bytes;)bytesモジュールのlength関数がインポートされて、メインモジュールのlengthがオーバーライドされてしまいます。

◆ポイント4

$bytes = bytes::length $utf8_str;

bytesモジュールのlengthを使用しています。これは、文字数でなくバイト数を返します。

◆ポイント5

$bytes = length unpack('a*',$utf8_str);

unpackで文字列$utf8_strをバイト列に変換して、メインモジュール(組み込み関数)のlength でそのバイト数を返します。

◆ポイント6

{
    use bytes;
    $bytes = length $utf8_str;
    print $bytes, "\n";
}

中カッコの中がスコープになり、この中だけbytesモジュールのlength関数が適用され、バイト数が取得できます。

◆ポイント7

use Encode;
Encode::_utf8_off($utf8_str);
$bytes = length $utf8_str;
print $bytes, "\n";
Encode::_utf8_on($utf8_str);

文字列のUTF8フラグをオフにするとバイト列になり、これをメインモジュールのlengthでバイト数を取得できます。
その後、UTF8フラグをオンに戻します。