解決済み

マルチバイト文字列の部分取得方法は?

  • 暇なときにでも
  • 質問No.2437071
  • 閲覧数1022
  • ありがとう数3
  • 気になる数0
  • 回答数6
  • コメント数0

お礼率 73% (61/83)

EUCコードでスクリプトを書いています。

substr() で部分文字列を取得すると、2バイト文字がきれいに丸まりません。

$s = "12月23日はABCの誕生日";
print substr($s, 0, 14);

"12月23日はABC"
または
"12月23日はABCの"
を取得したい時は、どうすればよいのでしょうか?
通報する
  • 回答数6
  • 気になる
    質問をブックマークします。
    マイページでまとめて確認できます。

質問者が選んだベストアンサー

  • 回答No.6

ベストアンサー率 50% (3003/5914)

use encoding 'euc-jp';

$s = "12月23日はABCの誕生日";
print my_substr($s, 0, 14);

sub my_substr($$$){
/* my_substr は、部分文字列を指定したバイト数幅で取り出す
** $str は、全角半角混在の文字列
** $start は、取り出す"文字"位置
** $len は、取り出す数(文字数でなくてバイト数)
** 取り出すバイト数を越えない(サンプルの'の'は含まない)
*/
my ($str, $start, $len) = @_;
my $work="";
my $count=0;
my $i;
my $strlen=length $str;
for($i=$start;$i<$strlen;$i++){
my $c = substr($str,$i,1);
$count += ord($c) < 256 ? 1 : 2 ;
last if $count > $len;
$work .= $c;
}
return $work;
}
お礼コメント
xespr

お礼率 73% (61/83)

さすが BLUEPIXY さん!
完璧です!お見事です。
素晴らしく大成功でした!
ありがとうございました!!
投稿日時 - 2006-09-28 18:46:51
OKWAVE 20th Be MORE ありがとうをカタチに

その他の回答 (全5件)

  • 回答No.5

ベストアンサー率 39% (59/150)

$n= 14;
$s=~/^((?:\x8E[\xA1-\xDF]|\x8F?[\xA1-\xFE][\xA1-\xFE]|[\x09-\x7E]){0,$n})/;
print $1,"\n";

#14文字以内にマッチなら上記のようになりますが、
#14文字ぴったりのときだけマッチさせるなら {$n} に書き換えてください。
補足コメント
xespr

お礼率 73% (61/83)

ありがとうございます。ダメでした。

$s = "12月23日はABCの誕生日";
foreach $i ( 3 .. 9 ) {
print "----\n\$i:$i\n";
# $s2 = my_substr($s, 0, $i);
$s2 = substr($s, 0, $i);
# $s2 =~ s/(\x8e|\x8f.|[^\x00-\x7F\x8e\x8f])$//;
# $s2 =~ s/(\x8e|\x8f.?|[^\x00-\x7F\x8e\x8f])$//;
$s2 =~ /^((?:\x8E[\xA1-\xDF]|\x8F?[\xA1-\xFE][\xA1-\xFE]|[\x09-\x7E]){0, $i})/; # $n -> $i に変えてます
print "\$s2:$s2" . "\n";
}
__END__
の実行結果

----
$i:3
$s2:12・
----
$i:4
$s2:12月
----
$i:5
$s2:12月2
----
$i:6
$s2:12月23
----
$i:7
$s2:12月23搭
----
$i:8
$s2:12月23日
----
$i:9
$s2:12月23日k
投稿日時 - 2006-09-28 18:44:26
  • 回答No.4

 #2です。

 んー。こっちじゃ特に問題なく出てますね……。(連結しても)

 s/(\x8e|\x8f.?|[^\x00-\x7F\x8e\x8f])$//;

 ↑これでどうですか?
補足コメント
xespr

お礼率 73% (61/83)

すみません。連結するしないは、私の確認ミスのようです。
色々試しすぎて混乱してしまったようです。すみませんでした。

どちらにしてもバグが表れるようです。


$s = "12月23日はABCの誕生日";

foreach $i ( 3 .. 25 ) {
print "----\n\$i:$i\n";
$s2 = substr($s, 0, $i);
# $s2 =~ s/(\x8e|\x8f.|[^\x00-\x7F\x8e\x8f])$//;
$s2 =~ s/(\x8e|\x8f.?|[^\x00-\x7F\x8e\x8f])$//;
# print "\$s2:$s2", "\n";
print "\$s2:$s2" . "\n";
}
__END__
投稿日時 - 2006-09-28 15:30:16
  • 回答No.3

ベストアンサー率 53% (94/175)

use encodingしましょう。
実行時に内部的にUTF-8に変換され、1バイト文字も2バイト文字も1文字として扱えるようになります。

use encoding 'euc-jp';
$s = "12月23日はABCの誕生日";
print substr($s, 0, 14);

結果:
12月23日はABCの誕生日
  • 回答No.2

 Perl 5.8 未満の場合の対応法です。
 substr() で取り出した文字列に対し、下記の正規表現を実行してください。

$s = "12月23日はABCの誕生日";
$s = substr($s, 0, 14);
$s =~ s/(\x8e|\x8f.|[^\x00-\x7F\x8e\x8f])$//;
print $s;

 テストしてませんが、多分巧くいくと思います。
 ロジックはEUC専用です。
お礼コメント
xespr

お礼率 73% (61/83)

ありがとうございます。
まさに求めていたご回答です。

しかしながら、

$s = "12月23日はABCの誕生日";
$s = substr($s, 0, 4);
$s =~ s/(\x8e|\x8f.|[^\x00-\x7F\x8e\x8f])$//;
print $s, "\n"; # 正しく見える
print $s . "\n"; # 連結すると末尾がおかしくなる

丸め後の文字列の末尾に、まだ何か付いているのでしょうか?
投稿日時 - 2006-09-28 14:17:43
  • 回答No.1

ベストアンサー率 45% (43/94)

perl5.8以上であれば、
use encoding “euc-jp”;
を使うと、EUC文字列でもsubstrが正しく動作するそうです。

それ以下のバージョンだと、substrで取り出した文字列の末尾を
調べてEUC1バイト目なら削除する、という処理を自前で行う
必要があるでしょう。
お礼コメント
xespr

お礼率 73% (61/83)

ありがとうございます。

use encoding “euc-jp”;
できれいに取得できました。

しかし、今回は、特定の印字サイズで取得したいのです。

$s1 = "あいうえお";
$s2 = "12345かきくけこ";

4桁なら下記のように取りたい
あい
1234

のですが、use encoding では
あいうえ
1234

となってしまいます。

「文字列の末尾を調べてEUC1バイト目なら削除する」
という技を教えていただける方、いらっしゃいますでしょうか?
投稿日時 - 2006-09-28 13:42:37
このQ&Aで解決しましたか?
AIエージェント「あい」

こんにちは。AIエージェントの「あい」です。
あなたの悩みに、OKWAVE 3,500万件のQ&Aを分析して最適な回答をご提案します。

関連するQ&A
こんな書き方もあるよ!この情報は知ってる?あなたの知識を教えて!
このQ&Aにはまだコメントがありません。
あなたの思ったこと、知っていることをここにコメントしてみましょう。

その他の関連するQ&A、テーマをキーワードで探す

キーワードでQ&A、テーマを検索する

特集


より良い社会へ。感謝経済プロジェクト始動

ピックアップ

ページ先頭へ