web-dev-qa-db-ja.com

ずっとUTF-8

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

エンコーディング/文字セットを正確にどこに設定する必要がありますか?これを行うには、Apache、MySQL、およびPHPを設定する必要があることを認識しています - 従うことができる標準的なチェックリストはありますか。

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

1096
mercutio

データストレージ

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

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

データアクセス

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

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

    • PHP≥5.3.6で PDO 抽象化レイヤーを使用している場合、 DSNcharsetを指定できます。

      $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_charset php.iniオプションを使用するか、手動でContent-Type MIMEヘッダーを発行できます。これは、より多くの作業ですが、同じ効果があります。

  • json_encode()を使用して出力をエンコードする場合、2番目のパラメーターとして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で安全なnotです通常のPHP文字列操作(連結など)で安全に実行できますが、ほとんどの場合、同等のmbstring関数を使用する必要があります。

  • あなたが何をしているのかを知るために(読みましょう:それを台無しにしないで)、あなたは本当にUTF-8とそれが可能な限り低いレベルでどのように機能するかを知る必要があります。 tf8.com のリンクのいずれかをチェックして、知っておく必要のあるすべてを学ぶための優れたリソースを探してください。

972
chazomaticus

chazomaticusの優れた答え :に1つ追加したいと思います。

METAタグも忘れないでください(このように、または HTML4またはXHTML版の )。

<meta charset="utf-8">

それは些細なように思えますが、IE7は私にそれ以前に問題を与えました。

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

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

編集:

W3Cは実際にはI18N専用のかなり大きな セクションを持っています 。この問題に関連した記事がいくつかあります。HTTP、(X)HTML、およびCSSに関するものです。

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

140
mercator

Php.iniでdefault_charsetを設定することに加えて、コードの中からheader()を使って正しい文字セットを送ることができます。

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

PHPでのUnicodeの使用は、ほとんどの 文字列関数がUnicodeで機能しないことを理解している限り簡単です。一部の文字列は完全に に変換されることがあります。 PHPは、「文字」を1バイト長と見なします。時にはこれで問題ありません(例えば、explode()はバイトシーケンスだけを探してそれを区切り文字として使います - だから実際にどんな文字を探すかは関係ありません)。しかし、その関数が実際にcharactersで動作するように設計されている場合、PHPはあなたのテキストがUnicodeで見つかるマルチバイト文字を持っているとは思わない。

チェックインするのに良いライブラリは phputf8 です。これはすべての「悪い」関数を書き換えるので、UTF-8文字列を安全に扱うことができます。 mbstringエクステンションのような、あなたのためにこれをやろうとするエクステンションもありますが、私はライブラリの方が移植性が高いので使用することを好みます(しかし私はマスマーケット製品を書くので、それは私にとって重要です)。しかし、とにかく、phputf8は舞台裏でmbstringを使用してパフォーマンスを向上させることができます。

59
chroder

古い話題、私は知っています。 PDOを使用している人に問題が見つかり、その答えはPDO接続文字列にこれを使用することでした。

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

私がこれを取ったサイトはダウンしていて、幸いにもグーグルキャッシュを使ってそれを得ることができました。

26
Brad F Jacobs

私の場合は、regexを使っているmb_splitを使っていました。そのため、mb_regex_encoding('UTF-8');を実行して、正規表現のエンコードがutf-8であることを手動で確認する必要もありました。

ちなみに、私はmb_internal_encoding()を実行して内部エンコーディングがutf-8ではないことを発見し、それをmb_internal_encoding("UTF-8");を実行して変更しました。

20
JDelage

あなたが5.3PHP未満であれば、まず第一にいいえ。あなたは取り組むべきたくさんの問題を抱えています。

intl ライブラリ、unicodegraphemes文字列操作localizationをサポートしているライブラリについては、誰も言及していません。そしてもっとたくさん、下記参照。

PHPでのUnicodeサポートについての情報をElizabeth Smithのスライド at PHPBenelux'14で引用します。

INTL

良い:

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

悪い:

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

mb_string

  • Zend_multibyteサポートを有効にします
  • 透過的なHTTPイン/アウトエンコーディングをサポート
  • Strtoupperなどのfuntionallityのためのいくつかのラッパーを提供します

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フィルタを使ってファイルをその場で変換できます。

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

19
Jimmy Kane

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

解決策は

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

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

13
Notflip

私がこれらの驚くべき答えに加える唯一のことはutf8エンコーディングであなたのファイルを保存することを強調することです、私はブラウザがあなたのコードエンコーディングとしてutf8を設定するよりこの特性を受け入れることに気づきました。たとえば、Notepad ++にはファイルエンコーディング用のメニューオプションがあり、現在のエンコーディングを表示して変更することができます。私のすべてのphpファイルには、BOMなしでutf8を使用します。

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

12
Puerto AGP

私はちょうど同じ問題を経験して、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());
}

ソースを見る

8

PHPでは、 マルチバイト関数 を使用するか、 mbstring.func_overload を有効にする必要があります。そのようにしてstrlenのようなものはあなたが1バイト以上かかる文字を持っているならうまくいくでしょう。

また、あなたの回答の文字セットを特定する必要があります。上記のようにAddDefaultCharsetを使用するか、ヘッダーを返すPHPコードを記述することができます。 (または、あなたのHTML文書にMETAタグを追加することもできます。)

8
JW.

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

簡単なのは、HTTPヘッダーやデータベースなどで文字セットを指定することだけですが、PHPコードが有効なUTF8を出力しない場合でも、問題はありません。それが難しい部分です、そしてPHPはあなたに事実上助けを与えません。 (私は、PHP6がこの最悪の問題を解決するはずだと思いますが、それでもまだしばらく時間がかかります)

6
jalf

一番上の答えは素晴らしいです。これは私が通常の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

それがすべてでした!

5
commonpike

MySQLサーバにクライアントとしてPHPではなく文字セットを決定させたい場合(古い動作、私の考えでは好ましい)、skip-character-set-client-handshakeの下のmy.cnf[mysqld]を追加し、mysqlを再起動します。

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

5

mysqlソリューションが必要な場合は、サーバーの移行後、私の2つのプロジェクトでも同様の問題がありました。多くの解決策を探して試した後、私はこれがうまくいく前に、この1つに出会いました。

mysqli_set_charset($con,"utf8");

この行を私の設定ファイルに追加すると、すべてうまくいきます。

私はこの解決策を見つけた https://www.w3schools.com/PHP/func_mysqli_set_charset.asp 私はHTMLクエリからの挿入を解決しようとしていたとき

がんばろう!

0
castro_pereira