ずっとUTF-8


1191

新しいサーバーをセットアップしていますが、WebアプリケーションでUTF-8を完全にサポートしたいと考えています。私は過去に既存のサーバーでこれを試しましたが、常にISO-8859-1にフォールバックする必要があるようです。

正確にどこにエンコーディング/文字セットを設定する必要がありますか?これを行うには、Apache、MySQL、およびPHPを構成する必要があることを認識しています。追跡できる標準のチェックリストはありますか、または不一致が発生した場所をトラブルシューティングしますか?

これは、MySQL 5、PHP 5、Apache 2を実行する新しいLinuxサーバー用です。


8
以下に、発生する可能性のあるすべてのエンコーディングエラーの概要を示します。sebastianviereck.de
Sebastian Viereck


PHP 7に関する最近のいくつかの議論は、2010年の「公式に放棄された」位置に変更がないことを示しています...「PHP7とUTF-8」について何か他にありますか?
Peter Krauss

この問題は一般的です。しかし、誰ショートカットソリューションが存在しない、あなたがセットアップする必要がありますutf-8のMySQL 5、PHP 5またはApache 2 - sepratelyそれらのそれぞれについて
マニッシュShrivastava

回答:


1016

データストレージ

  • utf8mb4データベースのすべてのテーブルとテキスト列に文字セットを指定します。これにより、MySQLはUTF-8でネイティブにエンコードされた値を物理的に保存および取得します。照合が指定されているutf8mb4場合utf8mb4_*(明示的な文字セットなしで)、MySQLは暗黙的にエンコーディングを使用することに注意してください。

  • MySQLの古いバージョン(<5.5.3)では、残念ながらutf8、単にUnicodeのサブセットのみをサポートするを使用する必要があります。冗談でしょ。

データアクセス

  • アプリケーションコード(PHPなど)では、使用するDBアクセス方法にかかわらず、接続文字セットをに設定する必要がありますutf8mb4。このように、MySQLは、アプリケーションにデータを渡したり、その逆を行ったりするときに、ネイティブUTF-8からの変換を行いません。

  • 一部のドライバーは、接続文字セットを構成するための独自のメカニズムを提供します。これは、独自の内部状態を更新し、MySQLに接続で使用されるエンコードを通知します。これは通常、推奨されるアプローチです。PHPの場合:

    • PHP≥5.3.6でPDO抽象化レイヤーを使用している場合charsetは、DSNで次のように指定できます。

      $dbh = new PDO('mysql:charset=utf8mb4');
    • mysqliを使用している場合は、次を呼び出すことができますset_charset()

      $mysqli->set_charset('utf8mb4');       // object oriented style
      mysqli_set_charset($link, 'utf8mb4');  // procedural style
    • 単純なmysqlで立ち往生しているが、PHP≥5.2.3を実行している場合は、を呼び出すことができますmysql_set_charset

  • ドライバが接続文字セットを設定するための独自のメカニズムを提供しない場合は、アプリケーションが接続上のデータをどのようにエンコードするのかをMySQLに通知するクエリを発行する必要がある場合がありますSET NAMES 'utf8mb4'

  • 上記と同じutf8mb4/ に関する考慮事項がutf8適用されます。

出力

  • アプリケーションがテキストを他のシステムに送信する場合は、文字エンコーディングも通知する必要があります。Webアプリケーションでは、データが送信されるエンコーディングを(HTTP応答ヘッダーまたはHTMLメタデータを通じて)ブラウザーに通知する必要があります。

  • PHPでは、default_charsetphp.iniオプションを使用するか、手動でContent-TypeMIMEヘッダーを手動で発行できます。

  • を使用して出力をエンコードする場合は、2番目のパラメーターとしてjson_encode()追加JSON_UNESCAPED_UNICODEします。

入力

  • 残念ながら、受け取ったすべての文字列は、保存したり、どこかで使用したりする前に、有効なUTF-8であることを確認する必要があります。PHP mb_check_encoding()がそのトリックを行いますが、それを忠実に使用する必要があります。悪意のあるクライアントが好きなエンコーディングでデータを送信できるため、これを回避する方法はありません。また、PHPでこれを確実に実行させるためのトリックは見つかりませんでした。

  • 現在のHTML仕様を読んだところ、次のサブ箇条書きは、現代のHTMLには不要であり、有効ではなくなっています。私の理解では、ブラウザはドキュメントで指定された文字セットで動作し、データを送信します。ただし、古いバージョンのHTML(XHTML、HTML4など)をターゲットにしている場合は、次の点が役立つ場合があります。

    • HTML5より前のHTMLの場合のみ:ブラウザーから送信されるすべてのデータをUTF-8にする必要があります。残念ながら、確実にこれを行う唯一の方法を使用する場合はaccept-charset、すべての<form>タグに属性を追加する必要があります<form ... accept-charset="UTF-8">
    • HTML5より前のHTMLの場合のみ:W3C HTML仕様では、クライアントはデフォルトで、サーバーが提供する任意の文字セットでサーバーにフォームを送信する必要があると述べていますが、これは明らかに推奨事項にすぎないため、すべての場合に明示する必要があります<form>鬼ごっこ。

その他のコードに関する考慮事項

  • もちろん、提供するすべてのファイル(PHP、HTML、JavaScriptなど)は、有効なUTF-8でエンコードする必要があります。

  • UTF-8文字列を処理するたびに、安全に処理することを確認する必要があります。残念ながら、これは難しい部分です。おそらく、PHPのmbstring拡張機能を広範囲に使用したいと思うでしょう。

  • PHPの組み込み文字列操作は、デフォルトではUTF-8セーフではありません 通常のPHP文字列操作(連結など)で安全に行えることはいくつかありますが、ほとんどの場合、同等のmbstring関数を使用する必要があります。

  • あなたが何をしているのかを理解するには(読んで、ごちゃごちゃにしないでください)、UTF-8と、それが最低限のレベルでどのように機能するかを知る必要があります。utf8.comからのリンクをチェックして、知っておくべきすべてのことを学ぶための優れたリソースを探してください。


4
照合をutf8_ *として指定すると、自動的にutf8としてもエンコードされることは私の理解です。これは間違っていますか?
chazomaticus 2008年

49
私は間違っていません:COLLATEは文字セットを意味します。たとえば、dev.mysql.com/doc/refman/5.0/en/charset-database.htmlを参照してください。
chazomaticus 2008年

7
文字セットを設定するためのPDOの例も追加することを検討してください。
ジャック

97
MySQLは他の人と同じ言語を話すわけではないことに注意してください。MySQLが「utf8」と言うとき、それは本当に「神のために3バイトに制限されている、UTF-8の奇妙に遅延されたバリアントは、とんでもない理由を知っている」ことを意味します。本当にUTF-8が必要な場合は、MySQLがutf8mb4を呼び出すのが好きなこの変なことをしたいことをMySQLに伝える必要があります。「WTF!」を節約する必要はありません。
R.マルティーニョフェルナンデス

4
この回答は非常に役立ちましたが、DBクエリの結果をajax経由で渡すときに、JSON_UNESCAPED_UNICODEをPHPのjson_encodeに追加する必要があることもわかりました。
Petay87

150

chazomaticusの素晴らしい答えに 1つ追加したいと思います

METAタグも忘れないでください(このように、またはHTML4またはXHTMLバージョンの METAタグなど)。

<meta charset="utf-8">

ささいなことのように思えますが、IE7は以前に問題を抱えていました。

私はすべてを正しくやっていました。データベース、データベース接続、およびContent-Type HTTPヘッダーはすべてUTF-8に設定されており、他のすべてのブラウザーで正常に機能しましたが、Internet Explorerは依然として「西ヨーロッパ」エンコーディングの使用を要求しました。

ページにMETAタグがないことがわかりました。これを追加することで問題は解決しました。

編集:

W3Cには、実際にはI18N専用のかなり大きなセクションがあります。彼らはこの問題に関連する記事をいくつか持っています–物事のHTTP、(X)HTML、CSS側を説明します:

彼らは、HTTPヘッダーとHTMLメタタグの両方(または、XHTMLがXMLとして提供される場合はXML宣言)の使用を推奨しています。


HTTPヘッダーで文字セットを指定することもできませんか?おそらく、Webサーバーの構成オプションが必要です...
oliver

2
@oliver:はい、HTTPヘッダーで送信できますが、クライアントがファイルを保存すると常にメタタグが保存されるため、コンテンツで送信することをお勧めします。HTTPヘッダーは、ブラウザーが保存済みファイルのメタタグにそれをコピーするのに十分スマートでない限り、単に消える可能性があります。

5
また、行がヘッド要素の最初の子であることを確認してください(Unicodeの前に)。ブラウザーは、上記のメタ要素にアクセスした後でページを再解釈する場合があります。
アレックス、

64

default_charsetphp.iniでの設定に加えてheader()、出力の前に、コード内から使用して正しい文字セットを送信できます。

header('Content-Type: text/html; charset=utf-8');

ほとんどの文字列関数がUnicodeで機能せず、一部の文字列が完全に文字化けする可能性があることを理解している限り、PHPでのUnicodeの操作は簡単です。PHPは「文字」を1バイトの長さと見なします。これは大丈夫な場合もあります(たとえば、explode()バイトシーケンスのみを検索してセパレーターとして使用するため、実際に検索する文字は関係ありません)。しかし、関数が実際に文字を処理するように設計されている場合、PHPは、テキストにUnicodeで検出されるマルチバイト文字が含まれていることを認識しません。

チェックインに適したライブラリはphputf8です。これにより、すべての「不良」関数が書き換えられるため、UTF8文字列を安全に処理できます。mbstring拡張機能のような拡張機能もありますが、これは移植性が高いためライブラリを使用することをお勧めします(ただし、私は大規模市場向けの製品を作成しているので、それは私にとって重要です)。しかし、phputf8は舞台裏でmbstringを使用してパフォーマンスを向上させることができます。


php.iniでオーバーロード設定を設定します。マルチバイト文字列を使用するときに役立ちます。
Anthony Rutledge 2015

32

私はPDOを使用している誰かに問題を見つけました、そして答えはPDO接続文字列にこれを使用することでした:

$pdo = new PDO(
    'mysql:host=mysql.example.com;dbname=example_db',
    "username",
    "password",
    array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));

これを取得したサイトはダウンしていますが、幸い、Googleのキャッシュを使用して取得できました。


1
これをもう少し探すと、これは5.3.6より前のPHPバージョンにのみ必要です。参照:http : //stackoverflow.com/a/4361485/2286722(ただし、別の$dbh->exec("set names utf8");;を使用していますが、ここで紹介する方法を使用することをお勧めします)。ところで PHPマニュアルのコメントとして、これに関する同様の注記があります:php.net/manual/en/pdo.construct.php#96325
Marten Koetsier、2015


24

私の場合、mb_split正規表現を使用するを使用していました。したがって、次のようにして、正規表現エンコーディングがutf-8であることを手動で確認する必要もありましたmb_regex_encoding('UTF-8');

補足として、mb_internal_encoding()内部エンコーディングがutf-8ではないことを実行することによっても発見し、を実行することによってそれを変更しましたmb_internal_encoding("UTF-8");


22

まず、5.3PHP未満の場合は、いいえ。取り組むべき問題が山ほどあります。

誰もintlライブラリーについて言及していないことに驚いています。これは、ユニコード書記素文字列操作ローカリゼーションなどの多くをサポートしています。以下を参照してください。

PHPBenelux'14でのElizabeth Smithの スライドによるPHPでのUnicodeサポートに関する情報を引用します

INTL

良い:

  • ICUライブラリのラッパー
  • 標準化されたロケール、スクリプトごとにロケールを設定
  • 数値のフォーマット
  • 通貨のフォーマット
  • メッセージのフォーマット(gettextの代わり)
  • カレンダー、日付、タイムゾーン、時間
  • 文字変換器
  • スプーフチェッカー
  • リソースバンドル
  • コンバーター
  • IDNサポート
  • 書記素
  • 照合
  • イテレータ

悪い:

  • zend_multibiteをサポートしていません
  • HTTP入出力変換をサポートしていません
  • 関数のオーバーロードをサポートしていません

mb_string

  • zend_multibyteサポートを有効にします
  • 透過的なHTTP in / outエンコーディングをサポート
  • strtoupperなどの機能のラッパーを提供します

ICONV

  • 文字セット変換のプライマリ
  • 出力バッファハンドラ
  • MIMEエンコード機能
  • 変換
  • 一部の文字列ヘルパー(len、substr、strpos、strrpos)
  • ストリームフィルター stream_filter_append($fp, 'convert.iconv.ISO-2022-JP/EUC-JP')

データベース

  • mysql:テーブルと接続(照合ではない)の文字セットと照合。また、mysqlを使用しないでください-msqliまたはPDO
  • postgresql:pg_set_client_encoding
  • sqlite(3):unicodeとintlのサポートでコンパイルされていることを確認してください

他のいくつかの問題

  • 3番目の部分の拡張子を使用しない限り、PHPおよびWindowsでUnicodeファイル名を使用することはできません。
  • exec、proc_open、その他のコマンドライン呼び出しを使用している場合は、すべてをASCIIで送信します
  • プレーンテキストはプレーンテキストではなく、ファイルはエンコードされています
  • iconvフィルターを使用すると、ファイルをその場で変換できます

追加された機能などが変更された場合に備えて、この回答を更新します。


2
はい、そうです。MysqliとPDOは、ネイティブドライバーを使用できます。また、--with-mysqli=mysqlnd --with-pdo-mysql=mysqlndオプション付きでphpをコンパイルする場合は、mysqlndドライバーを使用できます。
Alexander Yancharuk、2014

14

これらの驚くべき答えに追加する唯一のものは、ファイルをutf8エンコーディングで保存することに重点を置くことです。私は、ブラウザがコードエンコーディングとしてutf8を設定するのではなく、このプロパティを受け入れることに気付きました。適切なテキストエディタではこれが表示されます。たとえば、Notepad ++にはファイルエンコード用のメニューオプションがあり、現在のエンコーディングが表示され、それを変更できます。私のすべてのphpファイルについて、BOMなしでutf8を使用しています。

いつか誰かに、誰かが設計したphp / mysqlアプリケーションのutf8サポートを追加するように頼まれました。すべてのファイルがANSIでエンコードされていることに気付いたため、ICONVを使用してすべてのファイルを変換し、データベーステーブルをutf8 charsetとutf8_general_ciを照合し、接続後に「SET NAMES utf8」をデータベースアブストラクションレイヤーに追加し(5.3.6以前を使用している場合は、接続文字列でcharset = utf8を使用する必要があります)、phpマルチバイトを使用するように文字列関数を変更します同等の文字列関数。


13

最近使用strtolower()すると、特殊文字の後にデータが切り捨てられる問題が発生する可能性があることを発見しました。

解決策は使用することでした

mb_strtolower($string, 'UTF-8');

mb_はMultiByteを使用します。より多くの文字をサポートしますが、一般的に少し遅くなります。


9

私はちょうど同じ問題を経験し、PHPマニュアルで良い解決策を見つけました。

すべてのファイルエンコーディングをUTF8に変更してから、接続のデフォルトのエンコーディングに変更しました。これですべての問題が解決しました。

if (!$mysqli->set_charset("utf8")) {
    printf("Error loading character set utf8: %s\n", $mysqli->error);
} else {
   printf("Current character set: %s\n", $mysqli->character_set_name());
}

ソースを表示


2
作業中のページのエンコードの問題を理解するために1時間費やしましたが、通常は問題の解決に長けています。私はいつもこのページを参考にしており、あなたの答えは私を助けてくれました。私の賛成票を得ました。私の場合、機能set_charset('utf8mb4')しませんでしたが機能しました>set_charset("utf8")が、それは実際には他の回答には示されていませんでした。
Funk Forty Niner 2017年

@FunkFortyNiner注意:set_charset("utf8")動作する可能性がありますが、動作は異なります(utf8およびutf8mb4とmysqlのバージョン履歴の違いについての注意をご覧ください)。utf8 あなたがしなければならない場合、そしてあなたが何をしているのか知っている場合にのみ使用してください!
マーティンヘニングス2018

5つ星の解決策、私はテキストファイルを1行ずつ読んでいて?文字ごとに、utf8を使用して、ANSIではなくsave-asを実行しました。ありがとう。
Atef Farouk

8

PHPでは、マルチバイト関数を使用するか、mbstring.func_overloadをオンにする必要があります。これにより、1バイトを超える文字がある場合にstrlenのようなことが機能します。

また、応答の文字セットを識別する必要があります。上記のようにAddDefaultCharsetを使用するか、ヘッダーを返すPHPコードを記述できます。(または、HTMLドキュメントにMETAタグを追加できます。)


func_overload設定に関するヒント-既存のコードへの変更を最小限に抑えることができます。
Simon East

4
注意してください。一部のコードは、実際には標準の文字列関数の1文字あたり1バイトの性質に依存している場合があります。
JW。

上記の@JWのコメントで指摘されている問題のため、mbstring.func_overload機能はPHP 7.2で廃止されることに注意してください。したがって、最善のアドバイスは次のとおりです。はい、mbstring関数は必ず使用する必要がありますが、標準関数をマルチバイトとして機能させるためにオーバーロード機能を使用しないでください。
2017

6

PHPでのUnicodeサポートは、依然として大きな混乱です。ISO8859文字列(内部で使用)をutf8に変換することはできますが、Unicode文字列をネイティブで処理する機能がありません。つまり、すべての文字列処理関数が文字列を壊して破損します。したがって、適切なutf8サポートのために別のライブラリを使用するか、すべての文字列処理関数を自分で書き直す必要があります。

簡単なのは、HTTPヘッダーやデータベースなどで文字セットを指定することだけですが、PHPコードが有効なUTF8を出力しない場合は問題ありません。これは難しい部分であり、PHPはほとんど役に立ちません。(私はPHP6がこれの最悪の問題を修正することになっていると思いますが、それはまだしばらくの間です)


6

MySQLサーバがクライアントとして文字セット、およびないPHP決定したい場合は(以前の動作を、私の意見では、好ましい)、追加してみてくださいskip-character-set-client-handshakeあなたにmy.cnf、下に[mysqld]、再起動mysql

これは、UTF8以外を使用している場合に問題を引き起こす可能性があります。


5

最高の答えは素晴らしいです。これが私が通常のdebian / php / mysqlセットアップでしなければならなかったものです:

// storage
// debian. apparently already utf-8

// retrieval
// the mysql database was stored in utf-8, 
// but apparently php was requesting iso. this worked: 
// ***notice "utf8", without dash, this is a mysql encoding***
mysql_set_charset('utf8');

// delivery
// php.ini did not have a default charset, 
// (it was commented out, shared host) and
// no http encoding was specified in the apache headers.
// this made apache send out a utf-8 header
// (and perhaps made php actually send out utf-8)
// ***notice "utf-8", with dash, this is a php encoding***
ini_set('default_charset','utf-8');

// submission
// this worked in all major browsers once apache
// was sending out the utf-8 header. i didnt add
// the accept-charset attribute.

// processing
// changed a few commands in php, like substr,
// to mb_substr

それだけでした!


1

MySQLソリューションが必要な場合は、サーバーの移行後、2つのプロジェクトで同様の問題が発生しました。多くの解決策を検索して試した後、私はこれに遭遇しました/これが機能する前は何もありませんでした):

mysqli_set_charset($con,"utf8");

この行を私の設定ファイルに追加した後、すべてがうまくいきます!

HTMLクエリからの挿入を解決しようとしていたときに、このソリューションhttps://www.w3schools.com/PHP/func_mysqli_set_charset.aspを見つけました

幸運を!


1

ただのメモ:

ラテン語以外の文字がとして表示されるという問題に直面しています。?????????質問をすると、この標準的な質問への参照が原因で問題が解決し、すべてを試し??????????ましたMySQL

これは主に、間違った文字セットを使用してデータベースに挿入され、変換されて実際に疑問符文字に保存された古いデータでテストしいるためです?。つまり、元のテキストが永久に失われ、何を試しても取得でき???????ます。

この質問の答えから学んだことを新しいデータに再適用すると、問題を解決できる可能性があります。


0

テーブルを表示するときにこの問題が発生しました。これを各エコー出力変数に置くだけです:

<td><?php echo utf8_encode ($Local) ?></td>
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.