以下環境で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は残念ながら、ダブルクォーテーション有りのファイルと無しのファイルが混在しています。(同一ファイル内での混在は無い前提)
どのようにすれば部の文字を含んだファイルを正常に読み込めるでしょうか?
よろしくお願いします。
No.4ベストアンサー
- 回答日時:
はっきりしたことは分かりませんが、文字コードがShift_JISと認識されていないように思えます。
欧米製のソフトは大抵、デフォルトの文字コードがLatin-1です。
「部」をLatin-1で見ると「•”」となり、2文字目は「"」とは別ですが、何らかの変換で「"」になりえます。
この辺
「#tヴ鉛株渠券鵠飼諸数」
や、加えてこの辺
「%sン遠鞄挙兼酷雌藷嵩」
で同じ状況になればこれが関係していると考えられます。
この回答への補足
指定してもらったもの全部は試せてませんが、いくつか試してみたところ「部」と同じく分けられないという事象が発生しました。
"部"が"•”"と認識されてしまいダブルクォーテーション2つ並んでいるので何らかの変換が起こり""のエスケープ(CSVでは""は"を表すエスケープだったはず)になってげんざいの状況になっているという感じでしょうか…。
Linux CentOS 6.3 で
localedef -f SHIFT_JIS -i ja_JP ja_JP.sjis
をコマンドラインで実行
プログラムの中で
setlocale(LC_ALL, 'ja_JP.sjis');
を書きました。
(書かないと、""で囲まないCSVを一切認識しないので、setlocale設定は生きているかと思います。)
これ以外にしなければいけない設定があるのでしょうか?
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は、囲い文字のエスケープ文字として、なぜかデフォルトで円マークが指定されています。
これを「"」に変更したらどうなりますか?
この回答への補足
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, ',','"','"')
No.3
- 回答日時:
これは俺個人の意見で古い知識に基づいたものですので聞き流し程度でお願いします。
fgetcsvは その昔 挙動不審疑惑があり、そういうのを見たり自分で体験したりで、使わないようにしてます。
多分、適切に設定してやれば、問題ないのかもしれませんがー
fgetcsv関数内の ある意味ブラックボックス内で処理されることであり、中身が不透明なのが嫌だったので、結局 CSVを自前の関数作って処理したことがあります。
ちゃんとPHPの中身読めば分かることでブラックボックスでも何でもないのですが、自分で読むほどの気力はありません。
例えば
http://php-demo.e1blue.net/php/status/4
のようなものを自作してやっているということですよね。
やはり自分で作った方が良いのでしょうか?
No.2
- 回答日時:
ちょっと今手元に試す環境がないのとソースが記載されてないので憶測ですが、SJISとCP932(sjis-win)を混同されていて、文字をSJISでエンコードしようとされていませんか?
WindowsからのCSVであれば#1の方の様に'sjis-win'を使用しないと、色々とうまく動かなかった記憶があります。
この回答への補足
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-fil …
http://d.hatena.ne.jp/hnw/20090317
http://www.revulo.com/blog/20080304.html
ココらへんです。
この回答への補足
回答No.4から内部でlatin-1になっている関係で不具合が発生している可能性が高いことが分かりました。
UTF-8の場合も別の文字で同じような問題が発生することは有るのでしょうか?
内部的に問答無用でlatin-1にしているのか、一般的でない文字コードの場合(SJIS等)のみlatin-1でやって世界的に標準のコード(UTF-8等)はlatin-1でやっていないのでしょうか?
確認する何かいい方法は有りますか?
よろしくお願いいたします。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Excel(エクセル) エクセルのVBAについて とあるサイトのコードを参考に、CSVの文字化けを直すVBAを作成しているの 7 2022/11/04 14:15
- その他(プログラミング・Web制作) データ解析ソフトRでのファイル入力read.csvがエラーになります 7 2022/03/27 22:11
- C言語・C++・C# pythonのファイルの並びでの読み込みとリストについて 4 2022/04/13 03:52
- Excel(エクセル) PHPプログラムをエクセルに張り付けると検索ボックスがでてくる! 3 2022/05/08 07:10
- Access(アクセス) CSVファイルの「0落ち」にVBA 6 2023/02/02 15:27
- システム CSVファイルのマッピング処理の省力化 1 2022/11/24 00:01
- その他(プログラミング・Web制作) Python - Excel で Webからデータを連続取得したいのですが エラーが出ます 1 2023/07/06 20:08
- Visual Basic(VBA) VBAでの共有パスにつきまして 1 2023/03/04 17:24
- Visual Basic(VBA) vbaのループ処理について 6 2022/05/06 15:35
- その他(プログラミング・Web制作) Fortranでの出力ファイル 2 2023/03/21 21:25
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・【大喜利】【投稿~11/22】このサンタクロースは偽物だと気付いた理由とは?
- ・お風呂の温度、何℃にしてますか?
- ・とっておきの「まかない飯」を教えて下さい!
- ・2024年のうちにやっておきたいこと、ここで宣言しませんか?
- ・いけず言葉しりとり
- ・土曜の昼、学校帰りの昼メシの思い出
- ・忘れられない激○○料理
- ・あなたにとってのゴールデンタイムはいつですか?
- ・とっておきの「夜食」教えて下さい
- ・これまでで一番「情けなかったとき」はいつですか?
- ・プリン+醤油=ウニみたいな組み合わせメニューを教えて!
- ・タイムマシーンがあったら、過去と未来どちらに行く?
- ・遅刻の「言い訳」選手権
- ・好きな和訳タイトルを教えてください
- ・うちのカレーにはこれが入ってる!って食材ありますか?
- ・おすすめのモーニング・朝食メニューを教えて!
- ・「覚え間違い」を教えてください!
- ・とっておきの手土産を教えて
- ・「平成」を感じるもの
- ・秘密基地、どこに作った?
- ・【お題】NEW演歌
- ・カンパ〜イ!←最初の1杯目、なに頼む?
- ・一回も披露したことのない豆知識
- ・これ何て呼びますか
- ・初めて自分の家と他人の家が違う、と意識した時
- ・「これはヤバかったな」という遅刻エピソード
- ・これ何て呼びますか Part2
- ・許せない心理テスト
- ・この人頭いいなと思ったエピソード
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・好きなおでんの具材ドラフト会議しましょう
- ・餃子を食べるとき、何をつけますか?
- ・あなたの「必」の書き順を教えてください
- ・ギリギリ行けるお一人様のライン
- ・10代と話して驚いたこと
- ・大人になっても苦手な食べ物、ありますか?
- ・14歳の自分に衝撃の事実を告げてください
- ・家・車以外で、人生で一番奮発した買い物
- ・人生最悪の忘れ物
- ・あなたの習慣について教えてください!!
- ・都道府県穴埋めゲーム
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
PHPでFilename cannot be empty...
-
stat failed
-
2つの画像ファイルが異なるファ...
-
ファイルの行数取得
-
PHPのif文でその処理を途中で抜...
-
透過PNGが透過されない!!
-
3つ以上の論理積は利用可能なの...
-
Subversionのリポジトリの削除
-
php文字列中のシングルクオート...
-
【file_exists】ファイルが存在...
-
CFileDialogの最初のディレクト...
-
リンク先を隠す方法はないでし...
-
VBでメールを送る時のSMT...
-
powershell で書いたプログラム...
-
CSV出力にHTMLが入ってしまう
-
php,mysqlにて画像パス保存/表...
-
Prompt入力値をphp変数として取...
-
Localeクラスについて
-
コマンドプロンプトを指定のデ...
-
VB6.0とBASP21を用いてyahooメ...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
PHPでFilename cannot be empty...
-
2つの画像ファイルが異なるファ...
-
pdfファイルをメンバーのみに閲...
-
stat failed
-
phpのftp_get()でエラーが出ます。
-
ファイル名は、数字の「0」(...
-
ファイルの行数取得
-
PHPでAPI 出力はCSVを取得する...
-
XMLのロードに失敗する
-
アクセス毎にリンクを自動変更
-
AS3外部テキスト読み込み
-
別のサーバにあるファイルの存...
-
Smartyで吐き出されたhtmlソー...
-
TSVファイルをCSV形式に変換したい
-
PHP5のsimplexml_load_fileで取...
-
php アップロードファイルが*....
-
PHPで特定の文字列のみ抽出し、...
-
ファイルを開いたままオープン...
-
ファイルを白紙にする
-
日本語が文字化けしないよう読...
おすすめ情報