Shift-JISでのCSVファイル読み込みで「部」を含むデータが正常に取得できない問題

このQ&Aのポイント
  • PHP 5.3.3環境でShift-JISのCSVファイルを読み込むプログラムを作成しています。
  • 「部」を含むデータは正常に取得できない問題が発生しており、対処方法がわかりません。
  • 他のデータは正しく取得できており、ダブルクォーテーションの有無による違いも存在しています。
回答を見る
  • ベストアンサー

shift-jisでfgetcsv

以下環境でCSVファイルの読み込プログラムを作っていますが、上手くいかないケースがあり困っています。 PHP 5.3.3 サーバー Linux 読み込むCSVの文字コード ShiftJIS phpの文字コード UTF-8 当然ですが setlocale(LC_ALL, 'ja_JP.sjis'); の設定はしています。 正常に動くケース ・"(ダブルクォーテーション)で囲んでない場合 ・"(ダブルクォーターション)で囲んであるケースのほとんど 正常に動かないケース ・"(ダブルクォーテーション)で囲んであり最後の文字が「部」の場合(他にも有る可能性がありますが現状発見できているのはこれのみ) $data = getcsv($fileHandler, 0, ','); とした時に 元データが "a","テニス部","12345" の場合 $data[0]→a $data[1]→テニス部",12345" となります。 (何故か12345の頭の"はどこかに行きます。) これが "a","テニス社","12345" の場合は $data[0]→a $data[1]→テニス社 $data[2]→12345 と求めた結果になります。 また、 a,テニス部,12345 とクォーテーション無しの場合は $data[0]→a $data[1]→テニス社 $data[2]→12345 想定通りとなります。 部と"の組み合わせがいけないということはわかりますが何をどうすればいいのか見当がつきません。 Shift-JISの所謂「駄目文字」に部は入っていないですし…。 読み込みCSVは残念ながら、ダブルクォーテーション有りのファイルと無しのファイルが混在しています。(同一ファイル内での混在は無い前提) どのようにすれば部の文字を含んだファイルを正常に読み込めるでしょうか? よろしくお願いします。

  • PHP
  • 回答数7
  • ありがとう数1

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

  • ベストアンサー
  • SortaNerd
  • ベストアンサー率43% (1185/2748)
回答No.4

はっきりしたことは分かりませんが、文字コードがShift_JISと認識されていないように思えます。 欧米製のソフトは大抵、デフォルトの文字コードがLatin-1です。 「部」をLatin-1で見ると「•”」となり、2文字目は「"」とは別ですが、何らかの変換で「"」になりえます。 この辺 「#tヴ鉛株渠券鵠飼諸数」 や、加えてこの辺 「%sン遠鞄挙兼酷雌藷嵩」 で同じ状況になればこれが関係していると考えられます。

mentaiko2
質問者

補足

指定してもらったもの全部は試せてませんが、いくつか試してみたところ「部」と同じく分けられないという事象が発生しました。 "部"が"•”"と認識されてしまいダブルクォーテーション2つ並んでいるので何らかの変換が起こり""のエスケープ(CSVでは""は"を表すエスケープだったはず)になってげんざいの状況になっているという感じでしょうか…。 Linux CentOS 6.3 で localedef -f SHIFT_JIS -i ja_JP ja_JP.sjis をコマンドラインで実行 プログラムの中で setlocale(LC_ALL, 'ja_JP.sjis'); を書きました。 (書かないと、""で囲まないCSVを一切認識しないので、setlocale設定は生きているかと思います。) これ以外にしなければいけない設定があるのでしょうか?

その他の回答 (6)

  • bm_hiro
  • ベストアンサー率51% (200/388)
回答No.7

お礼してもらっていたのに、しばらくココ自体を見ていませんでした。 返事が遅くなってすみません。 昔 自分がやったのは結構乱暴な方法です。 CSVの一行の各文字列の中のカンマは事前に全角カンマに置換した上で、半角カンマでexplodeして、各文字列の前後にクォートがあった場合除去する。 という事を自前でやっただけです。 具体的に言うと↓みたいな感じです。 CSV : "てすと,てすと","abc","def" ↓ てすと,てすと abc def 文字列の中にクォートが入っていないのは前提でしたので使えた方法ですし、スマートなやり方ではないと思いますし、負荷的にどうなのかも分かりません。

回答No.6

VMware Playerで、似たような環境を作って試してみました。 ■OS:CentOS 6.4 (32bit) ■PHP:5.3.3(yum install phpで取得したもの) 質問者さん同様の方法でlocaledefして、sjisを作り、質問にある文字列のみを記載したcsvファイルを作り、 fgetcsvを試してみましたが、同様の症状は発生しませんでした。 何か別の場所に問題がある気がします。 ・PHPのバージョン ・OSのバージョン ・コンパイルが正常にいっているのかどうか ・コンパイラのバージョン(C言語のmblenが本当に正しく動いているのか) 気になることは結構色々ありますが、バージョンや特定の組合せによって動かないということであるとどうしようもありません。 上記のことから、文字コードを変更してから取得するなり、自前でパーサを作りなりしたほうが良いかもしれません。 どうしても気になるということであれば、一度VMwareを使うなり、VertualPCやVertualBoxを使うなりして、同じ環境を作った時に同じ現象が起こるのか試してみるしか無いですね。

回答No.5

少し調べた感じですと、C言語のmblen関数で、文字長を取っているというものに成っているようで、 mblen関数は、単純にシステムに設定されているlocale情報に依存して、動作するという挙動のため setlocaleしないと上手く動かないというもののようです。 つまるところ、setlocaleが上手く言っていないような気がするんですが、 setlocaleで一度ローケルをセットした後に、 <?php echo locale(LC_ALL, 0); ?> とすると設定したローケルを返しますが、コレはどの様な値が帰ってきますか? 不正な値をsetlocaleした後は、その設定が反映されず、setlocaleする前のローケルが帰ってくるようなのですが。 現在のPHP5.3.21あたりでいろいろ試していましたが、 ・Windows→エラーは出ない(そもそもローケル文字が全然違う) ・さくらサーバ(CentOS)→エラーは出ない という結果で、もしかしたらphpのバージョンを5.3の最新にしたら解消されるのではないかという気もします。 それと、fgetcsvは、囲い文字のエスケープ文字として、なぜかデフォルトで円マークが指定されています。 これを「"」に変更したらどうなりますか?

mentaiko2
質問者

補足

setlocaleする前と後に echo setlocale(LC_ALL,0); してみました。(localeではなくsetlocalで出てくるようでしたので…。) echo setlocale(LC_ALL, 0); setlocale(LC_ALL, 'ja_JP.sjis'); echo setlocale(LC_ALL, 0); 前:C 後:ja_JP.sjis が出力されるという結果になりました。 なお、localedefコマンドは以下のようにやっていますのでsjisが全部小文字でも問題無いと考えています。 localedef -f SHIFT_JIS -i ja_JP ja_JP.sjis また、囲い文字のエスケープ文字を"に変更してみましたが、結果は変わりませんでした。 以下のようにfgetcsvの部分をしました。 fgetcsv($fileHandler, 0, ',','"','"')

  • bm_hiro
  • ベストアンサー率51% (200/388)
回答No.3

これは俺個人の意見で古い知識に基づいたものですので聞き流し程度でお願いします。 fgetcsvは その昔 挙動不審疑惑があり、そういうのを見たり自分で体験したりで、使わないようにしてます。 多分、適切に設定してやれば、問題ないのかもしれませんがー fgetcsv関数内の ある意味ブラックボックス内で処理されることであり、中身が不透明なのが嫌だったので、結局 CSVを自前の関数作って処理したことがあります。 ちゃんとPHPの中身読めば分かることでブラックボックスでも何でもないのですが、自分で読むほどの気力はありません。

mentaiko2
質問者

お礼

例えば http://php-demo.e1blue.net/php/status/4 のようなものを自作してやっているということですよね。 やはり自分で作った方が良いのでしょうか?

回答No.2

ちょっと今手元に試す環境がないのとソースが記載されてないので憶測ですが、SJISとCP932(sjis-win)を混同されていて、文字をSJISでエンコードしようとされていませんか? WindowsからのCSVであれば#1の方の様に'sjis-win'を使用しないと、色々とうまく動かなかった記憶があります。

mentaiko2
質問者

補足

windowsの機種依存文字の部分でおかしい動作をしているわけではないですし、SJISからUTF-8への変換部分では(質問には書きませんでしたが)普通にmb_convert_encoding($data,"UTF-8","sjijs-win");としています。 そもそもそれをする前の段階で上手くカンマで区切れず配列がずれ込むという現象に悩んでいます。 良い解決方法がありましたらお願いします。

回答No.1

ja_JP.sjisが、ご利用のOSに存在しない場合は、ソレを追記してやる必要があります。 詳しくは、 http://www.softel.co.jp/blogs/tech/archives/2331 ここで公開されてました。 ただ、環境の依存性が高い為、システムを移行する場合等で問題が発生する可能性があります。 そこで、別のテンポラリファイルに、まるごとutf-8に文字コードを変換したものを作ってそれから再度fgetcsvをする方法がアリます。 <?php $content = file_get_contents('sjis-no-csv.csv'); $tmp = tmpfile(); //テンポラリファイルの作成(ファイルポインタです) fwrite($tmp, mb_convert_encoding($content, 'utf-8', 'sjis-win')); rewind($tmp); while($row = fgetcsv($tmp, 4096)){ //読み込み処理 var_dump($row); } ?> と言った具合に。 ただ、大容量なファイルが読み込まれた時にかなりパフォーマンスが良くないので、逐次読み込み時にそもそも文字コードが変換されてくれば良いのではないか、ということで、 「php://filter」を使って、ストリームフィルタをかましてやれば良いのではないかという方法。 <?php class sjis_to_utf8 extends php_user_filter{ public function filter($in, $out, &$consumed, $closing){ while($bucket = stream_bucket_make_writeable($in)){ $bucket->data = mb_convert_encoding($bucket->data, 'utf-8', 'sjis-win'); $consumed += $bucket->datalen; stream_bucket_append($out, $bucket); } return PSFS_PASS_ON; } } stream_filter_register('convert.sjis_to_utf8', 'sjis_to_utf8'); $fp = fopen('php://filter/read=convert.sjis_to_utf8/resource=test.csv', 'r'); while($row = fgetcsv($fp, 4096)){ var_dump($row); } ?> と言った具合です。 filterに標準で文字コード変換が無いので、sjisをutf8に変換するラッパーを作ってやり、ソレを読み込ませるという方法。 他にも都合の良いやり方なんかは、適当に調べてみて下さい。 作成にあたって参考にしたサイトは、 http://au1.php.net/manual/ja/function.stream-filter-register.php http://d.hatena.ne.jp/hnw/20090317 http://www.revulo.com/blog/20080304.html ココらへんです。

mentaiko2
質問者

補足

回答No.4から内部でlatin-1になっている関係で不具合が発生している可能性が高いことが分かりました。 UTF-8の場合も別の文字で同じような問題が発生することは有るのでしょうか? 内部的に問答無用でlatin-1にしているのか、一般的でない文字コードの場合(SJIS等)のみlatin-1でやって世界的に標準のコード(UTF-8等)はlatin-1でやっていないのでしょうか? 確認する何かいい方法は有りますか? よろしくお願いいたします。

関連するQ&A

  • EXCEL→CSV保存時のダブルクォーテーションについて

    下記のようにEXCELマクロにてEXCELファイルをCSVファイルに保存しようとしています。 NewBook.SaveAs Filename:="test.csv", FileFormat:=xlCSV, CreateBackup:=False このとき、セル値にダブルクォーテーションが含まれていると、出力CSVファイルの値がダブルクォーテーションで囲まれてしまいます。 ●ダブルクォーテーションが含まれていない場合 セル値→A列:ABC     B列:DEF CSVファイル→ABC,DEF ●ダブルクォーテーションが含まれている場合 セル値→A列:A"C     B列:DEF CSVファイル→"A"C",DEF 後者の場合もダブルクォーテーションで囲まれないようにするにはどうすればよいでしょうか。

  • CSVファイルへの文字列データの書き出しについて

    OS:WindowsXP、Excel:2003 です。 Excelマクロ(VBA)で、あるデータをCSVファイルに出力しているのですが、 文字列型(String)を書き出すと、必ずダブルクォーテーションで囲まれます。 CSVへ書き出すときは、そういう仕様になっているということは分かったのですが、 処理の都合上、どうしてもダブルクォーテーションで囲まずに書き出ししたいと思っています。 そういう場合にはどのようにすれば良いのか教えて下さい。 ちなみに、書き出しは「Write」を使っています。

  • Access2000でのCSV出力(エクスポート)

    お世話になります。 Access2000を利用して、あるテーブルデータをCSVファイルとしてエクスポートしようとしています。 そのときに、すべての要素を「"」(ダブルコーテーション)で囲み(「,」カンマで区切り)たいのですが、 データ型が文字型のときは設定で上手く囲めるものの、数値型では値がそのまま出力されてしまいます。 数値型のフィールドもダブルコーテーションで囲む方法はありませんでしょうか? よろしくお願いいたします。 例)"ああああ","A10000",100         ↓   "ああああ","A10000","100" ← ここ

  • CSVファイルからダブルクォーテーションを削除したい。VBS使用で。

    CSVファイルからダブルクォーテーションを削除したい。VBS使用で。 CSVファイルのダブルクォーテーションを外す方法が Wendy02さん回答の http://oshiete1.goo.ne.jp/qa3016163.html (タブ区切りデータからダブルコーテーションを外したい) を使用してみました。 〔使用前〕 "1","00030123","00000000000000000000","00000000000000000019","4220120","42201","","1" 〔使用後〕 1,00030123,00000000000000000000,00000000000000000019,4220120,42101,",1" 使用後、ダブルクォーテーションで囲まれていない項目があると、 ダブルクォーテーションが残ってしまいます。 .Pattern ="""([^""]+)""" の部分を変更すれば、良いと思うのですが教えていただけないでしょうか?

  • shift-jisをUTF-8に読み込ませるには?

    shift-jisにてcsv形式で保存しているテキストファイルの配列(カンマ区切りの3番目)から取り出したものをUTF-8にエンコードする方法をご教示賜りたくご質問させて頂きました。 $Pscrtfl = "./datas/pdtscrt.txt"; open (DT,"<$Pscrtfl"); @Pscrt = <DT>; close (DT); foreach $i (@Pscrt){ @iW = split(/\,/,$i); $Pscrtfl = "./datas/pdtscrt.txt のtxtに入ってる3番目のデータを取り出して、それをUTF-8文字コードに 変換して出力させたいと思っております。 現在のところ私のやってみたこと foreach(@Pscrt){ use Encode; Encode::from_to( $_, 'utf8', 'shiftjis' ); $Pscrtfl .= $_; } とエンコードかけてみるんですが。。。。やはりcsvファイルの文字がUTF-8に変更していないのか、 <!doctype html> <html><head><meta charset="UTF-8">でprintしようとすると、やはり文字化けしてしまいます。 これを文字化けしない方法がお分かりの方おられましたら、ぜひご教示下さいませ。

    • ベストアンサー
    • Perl
  • ExcelのVBAでCSVを読み込むと通貨になる

    Excel2010のVBAでCSVファイルを読み込んで処理させたいのですが、読み込んだデータをExcelが勝手に別のデータに変えてしまうので困っています。 例えば、123,456というデータは、フィールドにカンマがあるので、CSVでは"123,456"となりますが、これを以下のようなVBAのコードで読み込ませると、書式が通貨として扱われてしまって、データも123456になってしまいます。文字列として読み込まれていれば正しいデータとして読み込まれるように思うのですが、文字列として読み込ませる方法が分かりません。正しく元のデータ123,456のまま読み込ませるにはどうすればいいのでしょうか。 なお、CSVの各フィールドにカンマやダブルクォーテーションが混在していると、各フィールドに分割する処理が複雑でよく分からないので、下記のコードのようにExcelの機能でCSVを各フィールドに分割される方法で読み込ませたいです。 よろしくお願いします。 Workbooks.Open Filename:= "file.csv"

  • エクセルとcsvについて

    エクセルにて、文字の先頭と語尾にダブルコーテーションをつけて、csvファイルを作り、そのファイルをメモ帳でひらいたのですが、ダブルコーテーションが3つずつついていました。作った通り1つになる方法ご存じの方教えてください。

  • CSVファイル読み込み、書き込み時の一般的な方法

    いつもお世話になります。 下記CSVデータの場合の分割された結果を示していますが、データを分割する際、一般的には、 どのような考え方で分割するロジックを組めばよいでしょうか。 CSVファイル読み込み、書き込み時におけるルール。 (間違っている、漏れているなどありましたらご指摘頂ければと思います。) ・ダブルコーテーションで括る必要のあるデータ  以下のデータを含むデータ。   区切り文字、改行、ダブルコーテーション ・ダブルコーテーションが入るデータは、ダブルコーテーションでエスケープする  aa"bb ⇒ aa""bb   上記ルールに則ったCSVファイルの読み込み方。  123,aaaaa,ああいい,"13,55","aa""bb","ab(改行)ZZ","ああ"",いい"   ↓  データ1 123  データ2 aaaaa  データ3 ああいい  データ4 13,55  データ5 aa"bb  データ6 ab(改行)ZZ  データ7 ああ",いい    上記結果になるようにデータを分割すればいいのですが、データにダブルコーテーションがある 場合、書き込み時に付与、読み込み時に削除、という具合にデータの編集処理が入ってしまうが いいのか、何かのミスで、ダブルコーテーションがエスケープされておらず、カンマと連続で 定義されていた場合「",」、データの終わりを示してしまい、意図しない個所で分割されてしまうの ではないか、何かのミスでダブルコーテーションの始め、もしくは終わりの定義が漏れいた場合 (「・・・,"aaaa,・・・」「・・・,aaaa",・・・」など)、これも意図しない個所で分割されてしまう、 などなど、一般的にはどのようなロジックにしておけばよいのでしょうか。 そもそもロジックで全てを防ぐ事はできない、ということでしたら、事前にこれだけは決めておくべき、 というポイントがありましたら、教えて頂けると嬉しいです。 世の中には、CSVファイルを分割するためのライブラリなどが公開されていますが、 外部のライブラリを使用してはいけないプロジェクトの場合など、自分で自作する必要があります。 その際の一般的な考え方、注意点などを踏まえて作ることができれば、無用なバグを含ませる ことができずにすむので、皆様の考えを教えて頂きたいと思い、質問した次第です。 宜しくお願いします。

    • ベストアンサー
    • Java
  • EXCELにてダブルコーテーションの区切りを用いて保存したい。

    DB作成のアルバイトをしているものです。 CSVファイルを保存するときに、ダブルコーテーションで保存されずに困っています。 もともとダブルコーテーションで区切られたCSVファイルなのですが、保存するとダブルコーテーションが消えてしまいます。 どうしたらよいのでしょうか? 教えていただければと思います。 よろしくお願いします。

  • "文字列"? > CSVデータをMySQLにインポート時テキストデータのフォーマット

    CSVデータをMySQLにインポートする時に、データの中にテキストデータ(文字列)があるとします。 データを正しくインポートする為に、文字列をダブルクォーテーションやシングルクォーテーションで囲む必要はありますでしょうか。 記憶は定かではありませんが、「PostgreSQLの場合、文字列をクォーテーションで囲むとエラーになる。MySQLの場合は逆で、文字列をクォーテーションで囲まないとエラーになる。」みたいな話しを聞いたことがあるような気がします。 どなたか、教えていただければ幸いです。 どうぞよろしくお願いいたします。

    • ベストアンサー
    • MySQL