文字コードの判別ができない

このQ&Aのポイント
  • 特定の文字にて、mb_detect_encodingが正常な文字コードを返してこない現象が発生しています。
  • DBから取得したデータをエンコードする際に文字化けする問題があります。
  • 直接記述した場合には問題ないため、原因はDB取得に関連している可能性があります。
回答を見る
  • ベストアンサー

文字コードの判別ができない

現在、よくわからない現象が発生したので ここで質問させていただきます。 原因などわかる方いらっしゃいましたらご教授ください。 また、情報不足や説明不足などありましたらご指摘ください 補足させていただきます。 --------------現象-------------- 特定の文字にて、mb_detect_encodingが正常な文字コードを返してこない 以下、簡略化したソースになります。 // 説明のため簡略化しているので、 // このソースは正常に動作しない可能性があり // また同様の現象も発生しないと思います。 // DBからデータを取得 // DB接続はPEARを使用しています。 // $rsはクエリを投げ終わっているとします。 $row =& $rs->fetchAll( MDB2_FETCHMODE_ASSOC ); // この時点で、文字コードはEUC-JPとなっています。 // 取得したデータをブラウザ出力のため、エンコードする function_hoge( &$row ); // 関数の引き渡す // 以下関数の内容抜粋 Function function_hoge( &$array ) { // 配列の場合と配列じゃない場合があるので注意すること If ( Is_Array( $array ) ) { // 配列の場合、Foreachでループ Foreach ( $array as $key => $val ) { // 連想配列の可能性があるので再度配列かチェック If ( Is_Array( $array[$key] ) ) { // この場合、再帰処理を行う function_hoge( &$array[$key] ); } Else { echo 'A===='.function_encode( $val ); $array[$key] = function_encode( $val ); echo 'B===='.function_encode( $val ); } } } Else { // この場合、配列ではないので普通にエンコードしておく $array = function_encode( $array ); } return $array; } Function function_encode( $strIn ) { $strOut = $strIn; $strEncode = mb_detect_encoding( $strIn, 'EUC-JP,UTF-8,SJIS' ); // 返還前の文字コードと変換後の文字コードが違う場合のみ変換する If ( StrToUpper( $strEncode ) <> 'UTF-8' ) { $strOut = mb_convert_encoding( $strIn, 'UTF-8', $strEncode ); } return $strOut; } // function_encode 簡単に書くと、こんな感じの処理になっております。 ※変な箇所とかありますが、あまり気にしないでください。 ほかの処理とかもあるので、走り書きです。申し訳ございません。 このとき、レコードセットに格納されている値は最初はEUC-JPとなっています。 カラムに[hoge_name]というフィールドがあるのですがそこにはいろいろな文字列が格納されています。 ほかのカラムは英数字のみしか格納されていないので文字コードは問題ないのですが この[hoge_name]がちゃんとエンコードされたりされなかったりという状況です。 具体的に、以下のデータになります。 DBのデータが以下の場合 hoge_id hoge_name 1 経理 2 業務 3 法務 のようにデータがあった場合、3の法務だけ上記コードのA、Bの箇所でエンコードが ???な状態です。 1、2のデータはAの箇所でEUC-JPとなり、Bの箇所でUTF-8となり正常にエンコードされるのですが 3の法務の場合だけ、Aの箇所でUTF-8、Bの箇所でUTF-8となってしまいます。 上記処理で、変換したい文字コードと実際の文字コードが同じ場合には エンコードしないようになっているのですが、3の法務は実際にはEUCのため 出力時に文字化けしてしまいます。 ただし、これをもっと簡単に記述したとして mb_detect_encoding( '法務', 'EUC-JP,UTF-8,SJIS' ); のように、DB取得ではなく直接記述した場合には問題ありません このような現象をご存知、または原因がわかる方いらっしゃいましたらお教えください。

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

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

  • ベストアンサー
回答No.4

少し分かりづらかったようなので、補足いたします。 $row =& $rs->fetchAll( MDB2_FETCHMODE_ASSOC ); こちらで取得した連想配列$rowを function_hogeにて、連想配列を文字列一つの文字列まで展開し、再起処理にて一つづつ エンコードを掛けているものと思います。 それの一レコード分ずつを連結させてしまって、detect_encodingしてやれば誤検出が減るのではないかという 意見となります。 <?php //取得した$rowが下記のようなものと仮定する $row = array( array('id'=>1, 'busho'=>'法務', 'name'=>'ほげほげ', 'extr'=>'なんとか'), array('id'=>2, 'busho'=>'法務', 'name'=>'ああああ', 'extr'=>'いいいい'), array('id'=>3, 'busho'=>'法務', 'name'=>'うううう', 'extr'=>'ええええ'), array('id'=>4, 'busho'=>'法務', 'name'=>'わわわわ', 'extr'=>'かかかか'), ); //コレの1レコード文の配列を文字列に連結します。 $row_str = implode('', $row[0]); //result: 1法務ほげほげなんとか //連結した文字列をmb_detect_encoding $encoding = mb_detect_encoding($row_str, 'EUC-JP,UTF-8,SJIS' ); ?> ただ、取得してきた一レコード分というものがそれぞれ文字コードがバラバラなのであればどうしようもないですね。

shige_cust
質問者

お礼

hogehoge78さん 何度も何度もありがとうございます。 そして。。。この考え方 ちょっとびっくりしました! 思いつかなかったですね。。。 配列の場合、かなりの確立で誤検出をなくせそうですね! 前回のご回答いただいた内容をちゃんと理解していなかったです。 すみません 問題は、配列じゃなかったときですが。。。 でも、現状のロジック的には ブラウザからはUTFでわたってくるし問題ないかもです! ※まぁ、いたずらされたらとかセキュリティ的な面もありますが  そこはいったんスルーで。。。 これなら、回避できながら最小限の修正でいけるかも。 ちょっと組み込んでみます!

その他の回答 (4)

回答No.5

何度もごめんなさい。 文字コード検出がもう少し簡単に誤検出率を落とせそうな方法を考えてみました。 <?php function mb_detect_encoding_ex($str, $order='ASCII,JIS,UTF-8,EUC-JP,SJIS', $default=false){ if($default) return mb_detect_encoding($str, $order); //第三引数をtrueにすると通常のmb_detect_encodingをおこなう $enc = mb_detect_encoding($str, $order); if($enc == 'UTF-8'){ $s = "/(?:[\xbc][\xa3-\xe1]|[\xbe][\xa3-\xfe]|[\xc0-\xde][\xa1-\xbf]|[\xdf-\xf3][\xa1-\xfe]|[\xf4][\xa1-\xa6])/"; $str = preg_replace($s, '', $str); if(empty($str)) return mb_internal_encoding(); } return $enc; } ?> 解説しますと、mb_detect_encodingを掛けたときにUTF-8と検出された場合に、前回貼り付けさせていただきましたURLに記載されていた誤検出リストを元に作成した正規表現にてpreg_replaceでその範囲を削除します。 削除した場合に誤検出リストがemptyとなった場合、mb_internal_encodingで検出される文字コードを返します。(個々の部分は、return 'EUC-JP'としてもいいと思います。) ただ、UTF-8の文字のパターンでこの置換を行ってemptyになるパターンがあるかどうかは謎です(マルチバイトだと全角1文字で3バイトなので恐らく大丈夫だと思いますが・・・) 以上です。

shige_cust
質問者

お礼

hogehoge78さん こちらこそ、何度も迷惑をかけてしまい申し訳ございません。 そして、本当にありがとうございます。 上記ロジック、最初考慮していたのですが 以下の理由で挫折していました。 ・正規表現が苦手  ※困らない程度で使える程度なので。。 ・共通処理なので、できればロジック修正を最小限にしたい ただ、ご教授いただいたロジック、大変興味深く 検証に問題がでなければ、使わせていただくこともありそうです。 ※現状、即組み込みはちょっと不安が大きいですが。。。  同じく、UTFの3バイトとか不安が少し残りますし くどいかもしれませんが 何度も何度も本当にありがとうございます。

回答No.3

今回の件、少し気になったのでもう少し検証してみました。 結局のところ、「法務」とあった場合、 法務の後ろに何らかの文字を適当に挿入すると、正常な検出が出来るようなので、 データベースから取得した1レコードを、 $strEncode = mb_detect_encoding(implode('', $row), 'EUC-JP,UTF-8,SJIS'); としてやって、配列を展開する前に、文字コードチェックをするというのでは、どうでしょうか。 mb_detect_encodingを叩くタイミングで文字数が多ければ誤検出率も下がるのではないかと思います。

shige_cust
質問者

お礼

hogehoge78さん 何度もご回答ありがとうございます。 ご指摘のとおりなのですが、現在「法務」を「法務部」として対応しております。 ※ただし、これは局所的な対応であり  実際には誤検出され文字化けしている文字が多数あります。 またご教授いただいた手法も考慮していたのですが いかんせん、元の文字コードを判別できないので 付加する文字の文字コードを制御できないんですよね。。 たとえば、元がSJISの文字に対して、EUCの文字を付加するとおかしくなります。 この元の文字コードを判定し、SJISを付加してやれば誤検出の可能性は低くなりますが。。。それができないみたいですし。。。TT こないだの補足にて、出力時は EUC → UTFと書いてありますが なにせ、この処理使われている箇所が膨大なので。。 部分によっては、EUC以外 → UTFの可能性もあります。。。 文字数が多くなれば誤検出も下がるのは仰るとおりなのですが 元の文字コードを判別できないと、付加した文字列に 文字コードが混在する形になってしまいます。 ※現時点では、上記サンプルのように空文字で判定できるのか  未調査で申し訳ないのですが。。。 現時点でも解決策を模索しているので、 ご意見いただけるのは本当にありがたいです。

回答No.2

mb_detect_encodingの件、少し簡単で且つ乱暴なアドバイスでした。 申し訳ございません。 少し調べてみたところ、下記のサイトを発見いたしました。 http://www-ise3.ist.osaka-u.ac.jp/miura/?PHP%20GET%2FPOST%A5%E1%A5%BD%A5%C3%A5%C9%A4%C7%A4%CE%C6%FC%CB%DC%B8%EC%A4%CE%CA%B8%BB%FA%B2%BD%A4%B1%CB%C9%BB%DF#dde97368 ある特定の文字で、誤検出が発生するという記事です。 ここに書いてある解決方法の項目は、POSTデータの検出に関してなので、使えないとして、もし行うのであれば、 ■取得してきたものがHTMLであった場合 ⇒正規表現等で、METAタグのcharset情報を抜いてくる ■RSSなどXML情報であった場合 ⇒正規表現等で最初のXML宣言のcharsetを抜いてくる などといったところになると思います。

shige_cust
質問者

お礼

hogehoge78さん 回答ありがとうございます。 そのページではないですが、同じことが書いてあるページは 結構あって読み漁っていたのですが。。。 やはり無理なんですかね。。。 現状、エンコードするのは共通処理になっているため 変更箇所が膨大で。。。 その後、少し調べてみましたが 「法務」 ご教授いただいたページにも誤検出リストに含まれていましたが echo mb_detect_encoding( "法務", 'UTF-8,EUC-JP,SJIS' ); 簡単にこれだけの記述でソースコードをEUC、UTF-8にするだけで どっちの場合でもUTF-8という判定になってしまいますね。。。 ※この調査はしていませんでした。 ※くどいようですが、ソースコードをEUCにして動かすとUTF-8という判定になり  ソースコードをUTF-8にして動かすとUTF-8の検出となる(これは当たり前ですが) 解せないのが、最近になって発生しているってところなんですよね。。。 ※以前から発生していたのであればカットオーバー時から発生しているはずなので  もっと早くに(テスト時)などから発見されているはずですが。。。 根本的な対応方法を考えなきゃですね。。。 ※共通処理を見直すなど。。。TT  もしくは、DBをUTF-8で作り直してですかね。。。 せっかくいろいろ調べていただいたのに たいした情報、結果をお返しできなく残念です。 申し訳ございません。

回答No.1

質問者さんの必要としている回答と若干ずれるかもしれませんが、 mb_detect_encodingでの自動検出に関しまして、 所詮は与えられた文字列が「それっぽい」を検出しているだけに過ぎません。 その為、与えられた文字列が少なかった場合(1文字しかないとか)には、 当然誤検出を起こします。 また、MySQLなどのデータベースは、デフォルトの文字コードが定義されています。 mysql_real_escape_string関数といったようなSQLインジェクションを防ぐための関数に関しましては そのデフォルトで定義した(又はset names ...で定義した)文字コードを元にエスケープします。 その為環境によって動的に文字コードを変更しデータベースに保存するような状態というのは 問題があると思います。※mb_detect_encodingで文字コードを判別されているようなので、勝手に仮定しました。 つきまして、スクリプトとデータベースで文字コードが違うのであれば、 INSERTする前に スクリプトの文字コード⇒データベースの文字コード へ変換、 SELECTなどで出力の際に データベースの文字コード⇒スクリプトの文字コード へ変換と、 設定値でも設けて、決め打ちされたほうがよいと思います。

shige_cust
質問者

補足

hogehoge78さん 回答ありがとうございます。 仰るとおりですが、処理は基本的にエンコードを必ず行っております。 1.データ登録時に、UTF-8 → EUC 2.データ出力時に、EUC → UTF-8 基本はこの2つです。 上記以外の処理にて、HTTP通信でWEB上のデータを拾ってくる場合があるので 登録時のエンコード元がUTF-8で固定というわけではないのですが。。 このため、EUCに変換するべくmb_detect_encodingで毎回エンコードを調べて UTF-8に変換という処理としています。 > 所詮は与えられた文字列が「それっぽい」を検出しているだけに過ぎません。 こちら、そうなんですよね。。。 もっとちゃんと判別できる方法ってないのでしょうか。。。

関連するQ&A

  • 文字コード変換

    送信元のページがShift_JISの場合は正常に動作するのですが、 UTF-8等の場合は文字化けしてしまいます。 &jcode'convert(*val, 'sjis'); ↑のものを use Encode; Encode::from_to( $val, "UTF8", "Shift_JIS", Encode::XMLCREF ); このように書いたところ、UTF-8も文字化けしないのですが、 コードを自動的に認識して自動で振り分けさせる方法がわかりません。 サーバーのPerlのバージョンは5.8.6です。よろしくお願いいたします。

    • ベストアンサー
    • Perl
  • OSの文字コードを取得

    ココでも、googleでも検索したのですが みつけられなかったので教えてください。 Perlスクリプトを書いているのですが OSの文字コードを取得する方法があれば知りたいです。 具体的には Encode::from_to($aaa, "utf8","euc-jp"); という部分の"euc-jp"を、直で書きたくないのです。 my $os_encode = *****; として、なんとかうまく取得して Encode::from_to($aaa, "utf8",$os_encode); としたいのです。 OSの文字コードを取得する方法があれば教えてください。 よろしくお願いいたします。

  • Perlの文字コードを意識したファイルの読み込み方

    perl5.8でファイルを読み込む場合、文字コードを意識した以下のような呼び方("<:euc-jp")があるのですが この方法で読み込んだ$datの中身はperlの内部コード(UTF8)になっているということでしょうか? それとも、普通に("<")で読み込んで$dat = Encode::decode("euc-jp", $dat);とすべきでしょうか? use utf8; use Encode; my $fname = "test.txt"; # 文字コードがEUC-JP my $dat = ""; open(IN, "<:euc-jp", "$fname) or die "open error!"; flock(IN, 2); while(<IN>){ $dat .= $_; } close(IN);

    • ベストアンサー
    • Perl
  • Perl 文字コードについて

    From: $from To: $mailto CC: $mailcc Subject: $subject Mime-Version: 1.0 Content-type: text/plain;charset=\"UTF-8\" Content-Transfer-Encoding: 8bit この時点で、UTF-8を指定しました。 use Encode; use Encode::Guess; Encode::Guess->set_suspects( qw/ euc-jp shiftjis 7bit-jis / ); $name = encode('UTF-8', decode('Guess', $name)); で本文を、UTF-8に変換して送るようなコードを書きました。 これで一応、パソコン、スマホ共に「本文」は文字化けせず送れるのですが、 今度、別の問題が発生してしまったようで、 「件名」が文字化けしてしまうようになりました。 $subject = encode('UTF-8', decode('cp932', $subject)); 件名も、本文と同じように変換コードをしてみたところ、パソコンでは文字化けしませんでしたが「件名」で文字化けしてしまいます。 調べたところ本文と件名では、内容が違い MIMEエンコードを使用するとのことだったのですが、 実際どのように使うのかわからないです。 $subject = encode('ISO-2022-JP', decode('cp932', $subject)); encode('MIME-Header-ISO_2022_JP', $subject) このように記述するとスマホでは文字化けしないのですが パソコンで文字化けしてしまいます。 そこで、件名がパソコンとスマホで文字化けしないようにし、本文はUTF-8で送るようにするにはどのようにすればいいのでしょうか?

    • ベストアンサー
    • Perl
  • 文字コードの判別について

    とあるテキストファイルがあります。UNIX上で文字コードの判別を行いたいのですが、nkf --guess test.txtと打って調べると、 test.txt: ASCII (CRLF) となります。このファイルは、Shift-JISだという判定だと思います。 ファイルをUTF8に変換したいので、(1)Windows上のエディタ(秀丸)の機能を使ってUTF8に変換してからUNIX上にアップロードしたり、また(2)UNIX上から直接nkf -w --overwrite test.txtを実行して変換したりしたのですが、nkf --guess test.txtを打って調べると相変わらず test.txt: ASCII (CRLF) のままです。 色々調べた結果、ファイルの中身が英数字のみしか書かれていないので、nkfがこのファイルをShift JISだと「推測」しているということがわかってきました。確かに、試しにファイルの中身に日本語を書いて保存して実行してみると、ようやくnkf --guess test.txtの結果が test.txt: UTF-8 (CRLF) と表示されるようになりました。 これらの結果は、ファイルの中身のマルチバイト文字のコードを見て初めて文字コードがわかるようになるというように思えます。 私の理解ではファイル自体にEUCやShift JIS、UTF-8などの文字コードがあるものとばかり思っておりました。つまり、test.txtの内容が"This is a pen."であったとしてEUC、Shift JIS、UTF-8では全く異なる三種類のファイルが存在し得るものだと思っておりました。しかしこれは私の理解が間違っていて、英数字であれば常にShift JISであり、マルチバイト文字があって初めてUTF-8なのかEUCなのかShift JISなのかが決定されるということなのでしょうか? 英数字のみのファイルでもUTF-8ファイルという前提を確認する方法はないのでしょうか? 宜しくお願いします。

  • 文字コードの変換

    今、C言語の勉強をしているのですが、 EUC-JPの文字コードのファイルを読み込んでUTF-8の文字コードのファイルに変換したい場合はどうすればいいのでしょうか? それか、ファイルに書き込む時に文字コードをEUCーJPからUTFー8に変換して出力する方法でもいいです。 調べ足りないのかもしれませんが色々調べてもよくわかりませんでした。 よろしくお願いします。

  • TeraPadの文字コード

    TeraPadの文字コードについて教えてください。 1. TeraPadで扱える文字コードは Shift-JIS、JIS、EUC、Unicode、UTF-8、UTF-8N の6種類あるようですが、このEUCとEUC-JPは 同じものですか? 2. Shift-JISで保存すると、TeraPadの下の部分に 表示される文字コードがSJISとなります。 これはShift-JISのことですか? 3. 短い文章では文字コードを誤認識する場合も あるようですが、どうしたら正しく認識 させられますか? 指定した文字コードで保存できず困っています。

  • WWW::Mechanizeの文字コードについて質問

    WWW::Mechanizeの文字コードについて質問 以下のようなソースコードを書いて実行してみたのですが どのサイトを取得しても、すべて文字コードがUTF8だと認識されます。 ------------------------------------------------------- #!/usr/bin/perl #WWW:MechanizeでHTMLの取得 use WWW::Mechanize; $w = WWW::Mechanize->new(); $w->get("URL"); $html=$w->content; #文字コードの判別 use Encode; use Encode::Guess qw/ascii utf8 euc-jp shiftjis 7bit-jis/; my $dec = Encode::Guess->guess($html); print$dec->name; ------------------------------------------------------- 例えば以下のサイトは文字コードがEUC-JPですがこれもprintされるのはUTF8となってしまいます。 http://barukanlog.blog31.fc2.com/blog-entry-541.html WWW:Mechanizeでサイトを取得し、サイトの文字コードを調べてすべてsjisにする ということがしたいのですが、すべてutf8に判断されて先へ進めないんです。 何か設定やメソッドを追加しなくてはいけないのでしょうか? わかりにくい質問かとは思いますがご回答お願いします。

  • 文字列は大文字のまま、UTF-8エンコードの英字だけ小文字にしたい

    下記の方法でUTF-8エンコードをしたときに、 コードの英字が大文字になってしまい困っています。 use Encode; $str = "HOGEホゲ" $str =~ s/(\W)/'%' . unpack("H2", $1)/eg; print "$str"; 現状のエンコード後)HOGE%E3%83%9B%E3%82%B2 希望のエンコード後)HOGE%e3%83%9b%e3%82%b2 半角英数字(HOGE)は大文字のままで、 エンコードだけを小文字で書き出す簡単な方法はありますでしょうか。 (小文字でないと受け付けてくれないサイトに飛ばしたいため) 現状は正規表現でAからZまで26行繰り返しています。 $str =~s/\%([A-Z0-9]{1})?A/%$1a/g; ~ $str =~s/\%([A-Z0-9]{1})?Z/%$1z/g; よろしくお願いいたします。

    • ベストアンサー
    • Perl
  • 連想配列の文字コードを変換

    あるクラスから渡された連想配列値の文字コードはUTF-8です。 連想配列に格納されている値の文字コードをSJISに変換したいのですが、単に md_convert_encoding($array_values,'sjis','auto') ではうまくできません。 どうすれば変更できるのでしょうか?

    • ベストアンサー
    • PHP