プロが教える店舗&オフィスのセキュリティ対策術

文字コードがShift-JISのCSVファイルを読み込み、UTF-8のテキストファイルに出力するのに
プログラムの中で変更しようとしているのですが、うまくいきません。出力ファイルの文字コードを
確認するとShift-JISのままです。
どなたか教えていただけないでしょうか?
ActivePerl v5.16.0を使用し、Encodeモジュールのfrom_toを使用しています。

#!/usr/bin/perl

use strict;
use warnings;

use utf8;
use Encode;

my $input_file="input.csv";
my $output_file="output.txt";
open (IN, $input_file) or die "$!";
open (OUT, ">$output_file") or die "$!";

while (<IN>){
chomp ($_);
my @data=split(/,/,$_);

for(my $i=0;$i<@data;$i++){
$data[$i]=Encode::from_to($data[$i],'shiftjis','utf8'); #Shift-JISからUTF-8に変換
$data[$i]=~s/\s+//g;
print OUT $_;
}
print OUT "\n";
}
close (IN);
close (OUT);

A 回答 (5件)

あの、私のや他の回答をよく読んで考えてください。




for(my $i=0;$i<@data;$i++){
$data[$i]=Encode::from_to($data[$i],'shiftjis','utf8'); #Shift-JISからUTF-8に変換
$data[$i]=~s/\s+//g;
print OUT $_;
}

この部分は **** $_には何の影響も与えません ****
よって、** 出力に関することだけに注目したら **

for(my $i=0;$i<@data;$i++){
print OUT $_;
}

これと等価です。どこで「Shift_JIS からUTF-8へ変換」してますか?
$_は「while (<IN>){」の<IN>で1行読み込まれ、「chomp ($_);」で末尾の改行コードが削除されただけで、移行なにも変化していません。コードは入力のまま=Shift_JISです。それをそのまま出力すればShift_JISになるのが正解です。
しかも、項目数分だけ繰り返し出力されます。
(重複行になる、と#1に書いたのはchompのことを失念していた私のミスです)

@dataを変更したのなら、出力するのは@dataでしょう。
join(",", @data)とすれば、項目をカンマ区切りの文字列にすることができます。


あと#2にあったfrom_toの使い方。マニュアルをよく読みましょう
http://perldoc.perl.org/Encode.html#[$length-=]-from_to($octets,-FROM_ENC,-TO_ENC-[,-CHECK])
・$octetsを直接変換する
・$octetsの長さを返す
とあります。つまり
$data[$i]=Encode::from_to($data[$i],'shiftjis','utf8')
だと,$data[$i]には元の内容は破棄されて、文字列の長さになってしまいます。



各項目毎に処理したい、という意図はわかりました。

ですが、文字コードの変換が項目毎に違うなんてことはまず無いでしょう。
それならば、$_で1行をコード変換→splitして項目毎の処理、としてもいいのでは?

ついでにPerlIOを使って
open (IN, "<:encoding(shift_jis)", $input_file) or die "$!";
open (OUT, ">:utf8", $output_file) or die "$!";
とでもやれば、プログラム中はコードをあまり意識せずに文字列処理ができます。
    • good
    • 2
この回答へのお礼

kmeeさん

詳しい親切な回答ありがとうございます。
勉強になりました。

Encode:from_toについて書いてあった何人かのHPを読んでいて
同じ使い方をやっていたとのそれを鵜呑みにしてしようしていました。
これからはPerldocを参考にしたいと思います。

本当にありがとうございました。

お礼日時:2012/07/18 14:39

んーー・・・読めない人なのかねー



本質的に以下と同じことをしてるって指摘されてるんだけどなー

$a=1;
$b = $a + 1;
print $a; ## $aに1を足してるのになぜか 1 って表示されるっ!?

もしかして,@dataを$_のリファレンスみたいなものだと思ってる?

#nkfとかiconvを使う方がいいかもね.
    • good
    • 0

もう一度 2 つの回答をよく読んだら。

    • good
    • 1

#1 で何を言われたのか理解できているんでしょうか?



このプログラムは入力をほぼそのまま出力しています. つまり, 「文字コードがShift-JISのCSVファイルを読み込」んだら「出力ファイルの文字コードを確認するとShift-JISのまま」なのは当然です. むしろ, このプログラムでなぜ「UTF-8のテキストファイルに出力する」と思ったのでしょうか?

実際のプログラムが (特に出力の部分で) こうなっていないというなら, 実際のプログラム (に近いもの) を出してください.

あと, 「文字コードの変換方法」について勘違いしている. Encode::from_to の使い方を確認してください.

この回答への補足

Tacosanさん

回答ありがとうございます。
質問したプログラムは単に文字コードがShift-JISである入力ファイルを文字コードがUTF-8である出力ファイルに変換するプログラムを作成したいだけです。

私が考えたのは下の箇所で

for(my $i=0;$i<@data;$i++){
$data[$i]=Encode::from_to($data[$i],'shiftjis','utf8'); #Shift-JISからUTF-8に変換
$data[$i]=~s/\s+//g;
print OUT $_;
}

(別に1行ずつencodeすればよかったですが、)
Shift-JISのデータをUTF-8のデータに変換でき、UTF-8の出力ファイルに格納できると
思っていましたができませんでした。
何かいいやりかたはありますか?

補足日時:2012/07/13 19:44
    • good
    • 0

> my @data=split(/,/,$_);


で$_を分割して新しく作った@dataに対して処理しているのですから
@dataを変更しても $_には影響ありません。

・現状、同じ行が複数出力されていませんか?1行の項目数分、$_の出力を繰り返すのですから。
・そもそもsplitする意味はあるのでしょうか?

この回答への補足

kmeeさん

いろいろ不備があり、すいません。このプログラムの目的はカンマ区切りになっている項目をそれぞれ抽出し、いろいろ手を加えたいと思っているため、そうなっています。その部分はうまくいっていて、今は文字コードの部分だけがうまくいかない状況です。
言葉足らずですいませんでした。

補足日時:2012/07/11 20:03
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!