UTF-8 xuyên suốt
Tôi đang thiết lập một máy chủ mới và muốn hỗ trợ đầy đủ UTF-8 trong ứng dụng web của mình. Tôi đã thử điều này trước đây trên các máy chủ hiện có và dường như luôn phải quay lại ISO-8859-1.
Chính xác thì tôi cần đặt mã hóa / ký tự ở đâu? Tôi biết rằng tôi cần định cấu hình Apache, MySQL và PHP để thực hiện việc này - có danh sách kiểm tra tiêu chuẩn nào mà tôi có thể làm theo hoặc có thể khắc phục sự cố khi xảy ra sự không khớp không?
Điều này dành cho một máy chủ Linux mới, chạy MySQL 5, PHP, 5 và Apache 2.
Trả lời
Lưu trữ dữ liệu :
Chỉ định bộ
utf8mb4
ký tự trên tất cả các bảng và cột văn bản trong cơ sở dữ liệu của bạn. Điều này làm cho MySQL lưu trữ vật lý và truy xuất các giá trị được mã hóa nguyên bản trong UTF-8. Lưu ý rằng MySQL sẽ ngầm sử dụngutf8mb4
mã hóa nếuutf8mb4_*
đối chiếu được chỉ định (không có bất kỳ bộ ký tự rõ ràng nào).Trong các phiên bản MySQL cũ hơn (<5.5.3), bạn sẽ không may bị buộc phải sử dụng đơn giản
utf8
, chỉ hỗ trợ một tập hợp con các ký tự Unicode. Tôi ước tôi đang đùa.
Quyền truy cập dữ liệu :
Trong mã ứng dụng của bạn (ví dụ: PHP), trong bất kỳ phương pháp truy cập DB nào bạn sử dụng, bạn sẽ cần đặt bộ ký tự kết nối thành
utf8mb4
. Bằng cách này, MySQL không chuyển đổi từ UTF-8 gốc của nó khi nó chuyển dữ liệu cho ứng dụng của bạn và ngược lại.Một số trình điều khiển cung cấp cơ chế riêng để định cấu hình bộ ký tự kết nối, cả hai đều cập nhật trạng thái bên trong của chính nó và thông báo cho MySQL về mã hóa sẽ được sử dụng trên kết nối — đây thường là cách tiếp cận được ưu tiên. Trong PHP:
Nếu bạn đang sử dụng lớp trừu tượng PDO với PHP ≥ 5.3.6, bạn có thể chỉ định
charset
trong DSN :$dbh = new PDO('mysql:charset=utf8mb4');
Nếu bạn đang sử dụng mysqli , bạn có thể gọi set_charset():
$mysqli->set_charset('utf8mb4'); // object oriented style mysqli_set_charset($link, 'utf8mb4'); // procedural style
Nếu bạn gặp khó khăn với mysql đơn giản nhưng tình cờ đang chạy PHP ≥ 5.2.3, bạn có thể gọi mysql_set_charset.
Nếu tài xế không cung cấp cơ chế riêng của mình để thiết lập các bộ ký tự kết nối, bạn có thể phải đưa ra một truy vấn để nói MySQL như thế nào ứng dụng của bạn hy vọng dữ liệu trên các kết nối được mã hóa: SET NAMES 'utf8mb4'.
Việc xem xét tương tự đối với
utf8mb4
/utf8
áp dụng như trên.
Đầu ra :
Nếu ứng dụng của bạn truyền văn bản đến các hệ thống khác, chúng cũng sẽ cần được thông báo về mã hóa ký tự. Với các ứng dụng web, trình duyệt phải được thông báo về kiểu mã hóa mà dữ liệu được gửi (thông qua tiêu đề phản hồi HTTP hoặc siêu dữ liệu HTML ).
Trong PHP, bạn có thể sử dụng default_charsettùy chọn php.ini hoặc tự phát hành
Content-Type
tiêu đề MIME theo cách thủ công , thao tác này chỉ tốn nhiều công sức hơn nhưng có cùng tác dụng.Khi mã hóa đầu ra bằng cách sử dụng
json_encode()
, hãy thêmJSON_UNESCAPED_UNICODE
dưới dạng tham số thứ hai.
Đầu vào :
Thật không may, bạn nên xác minh mọi chuỗi nhận được là UTF-8 hợp lệ trước khi bạn cố gắng lưu trữ hoặc sử dụng nó ở bất cứ đâu. PHP mb_check_encoding()thực hiện được thủ thuật, nhưng bạn phải sử dụng nó một cách tôn giáo. Thực sự không có cách nào để giải quyết vấn đề này, vì các máy khách độc hại có thể gửi dữ liệu bằng bất kỳ kiểu mã hóa nào họ muốn và tôi chưa tìm ra mẹo nào để khiến PHP thực hiện việc này một cách đáng tin cậy.
Từ việc đọc thông số kỹ thuật HTML hiện tại của tôi , các dấu đầu dòng sau không cần thiết hoặc thậm chí còn hợp lệ đối với HTML hiện đại. Sự hiểu biết của tôi là các trình duyệt sẽ làm việc với và gửi dữ liệu trong bộ ký tự được chỉ định cho tài liệu. Tuy nhiên, nếu bạn đang nhắm mục tiêu các phiên bản HTML cũ hơn (XHTML, HTML4, v.v.), những điểm này có thể vẫn hữu ích:
- Đối với HTML chỉ trước HTML5 : bạn muốn tất cả dữ liệu do trình duyệt gửi cho bạn ở dạng UTF-8. Thật không may, nếu bạn đi bằng cách duy nhất để chắc chắn làm điều này là thêm
accept-charset
thuộc tính cho tất cả các bạn<form>
thẻ:<form ... accept-charset="UTF-8">
. - Đối với HTML trước HTML5 chỉ : lưu ý rằng thông số kỹ thuật HTML của W3C nói rằng máy khách "nên" mặc định gửi biểu mẫu trở lại máy chủ trong bất kỳ bộ ký tự nào mà máy chủ phân phối, nhưng đây rõ ràng chỉ là một đề xuất, do đó cần phải rõ ràng trên từng
<form>
nhãn.
- Đối với HTML chỉ trước HTML5 : bạn muốn tất cả dữ liệu do trình duyệt gửi cho bạn ở dạng UTF-8. Thật không may, nếu bạn đi bằng cách duy nhất để chắc chắn làm điều này là thêm
Cân nhắc mã khác :
Rõ ràng là đủ, tất cả các tệp bạn sẽ phân phát (PHP, HTML, JavaScript, v.v.) phải được mã hóa bằng UTF-8 hợp lệ.
Bạn cần đảm bảo rằng mỗi khi bạn xử lý chuỗi UTF-8, bạn làm như vậy một cách an toàn. Thật không may, đây là phần khó. Có thể bạn sẽ muốn sử dụng rộng rãi mbstringtiện ích mở rộng của PHP .
Các hoạt động chuỗi tích hợp của PHP theo mặc định không phải là UTF-8 an toàn. Có một số điều bạn có thể làm một cách an toàn với các hoạt động chuỗi PHP bình thường (như nối), nhưng đối với hầu hết mọi thứ, bạn nên sử dụng
mbstring
hàm tương đương .Để biết bạn đang làm gì (đọc: không làm rối nó lên), bạn thực sự cần biết UTF-8 và cách nó hoạt động ở mức thấp nhất có thể. Kiểm tra bất kỳ liên kết nào từ utf8.com để biết một số tài nguyên tốt để tìm hiểu mọi thứ bạn cần biết.
Tôi muốn thêm một điều vào câu trả lời xuất sắc của chazomaticus :
Đừng quên thẻ META (như thẻ này, hoặc phiên bản HTML4 hoặc XHTML của nó ):
<meta charset="utf-8">
Điều đó có vẻ tầm thường, nhưng IE7 đã gây ra cho tôi vấn đề với điều đó trước đây.
Tôi đã làm mọi thứ đúng; cơ sở dữ liệu, kết nối cơ sở dữ liệu và tiêu đề HTTP Loại Nội dung đều được đặt thành UTF-8 và nó hoạt động tốt trong tất cả các trình duyệt khác, nhưng Internet Explorer vẫn khăng khăng sử dụng mã hóa "Tây Âu".
Hóa ra trang bị thiếu thẻ META. Thêm vào đó đã giải quyết được vấn đề.
Biên tập:
W3C thực sự có một phần khá lớn dành riêng cho I18N . Họ có một số bài viết liên quan đến vấn đề này - mô tả khía cạnh HTTP, (X) HTML và CSS của những thứ:
- Câu hỏi thường gặp: Thay đổi mã hóa trang HTML (X) thành UTF-8
- Khai báo mã hóa ký tự trong HTML
- Hướng dẫn: Bộ ký tự & mã hóa trong XHTML, HTML và CSS
- Đặt thông số bộ ký tự HTTP
Họ khuyên bạn nên sử dụng cả tiêu đề HTTP và thẻ meta HTML (hoặc khai báo XML trong trường hợp XHTML được phân phát dưới dạng XML).
Ngoài cài đặt default_charset
trong php.ini, bạn có thể gửi bộ ký tự chính xác bằng cách sử dụng header()
từ trong mã của mình, trước bất kỳ đầu ra nào:
header('Content-Type: text/html; charset=utf-8');
Làm việc với Unicode trong PHP rất dễ dàng miễn là bạn nhận ra rằng hầu hết các hàm chuỗi không hoạt động với Unicode và một số có thể xử lý chuỗi hoàn toàn . PHP coi các "ký tự" có độ dài 1 byte. Đôi khi điều này không sao cả (ví dụ: explode()
chỉ tìm kiếm một chuỗi byte và sử dụng nó làm dấu phân tách - vì vậy bạn tìm kiếm ký tự thực tế nào không quan trọng). Nhưng những lần khác, khi hàm thực sự được thiết kế để hoạt động trên các ký tự , PHP không biết rằng văn bản của bạn có các ký tự nhiều byte được tìm thấy với Unicode.
Một thư viện tốt để kiểm tra là phputf8 . Điều này viết lại tất cả các hàm "xấu" để bạn có thể làm việc an toàn trên các chuỗi UTF8. Có những tiện ích mở rộng như tiện ích mở rộng mbstring cũng cố gắng làm điều này cho bạn, nhưng tôi thích sử dụng thư viện hơn vì nó dễ di động hơn (nhưng tôi viết các sản phẩm đại chúng, vì vậy điều đó quan trọng đối với tôi). Nhưng dù sao thì phputf8 cũng có thể sử dụng mbstring để tăng hiệu suất.
Tôi đã tìm thấy sự cố với ai đó sử dụng PDO và câu trả lời là sử dụng điều này cho chuỗi kết nối PDO:
$pdo = new PDO(
'mysql:host=mysql.example.com;dbname=example_db',
"username",
"password",
array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
Trang web tôi lấy nó từ đã bị lỗi, nhưng tôi đã có thể lấy nó bằng cách sử dụng bộ nhớ cache của Google, thật may mắn.
Trong trường hợp của tôi, tôi đã sử dụng mb_split
, sử dụng regex. Do đó, tôi cũng phải đảm bảo mã hóa regex là utf-8 theo cách thủ công bằng cách thực hiệnmb_regex_encoding('UTF-8');
Lưu ý thêm, tôi cũng phát hiện ra khi chạy mb_internal_encoding()
mã hóa bên trong không phải là utf-8 và tôi đã thay đổi điều đó bằng cách chạy mb_internal_encoding("UTF-8");
.
Trước hết nếu bạn ở <5.3PHP thì không. Bạn có rất nhiều vấn đề cần giải quyết.
Tôi ngạc nhiên là không ai đề cập đến thư viện intl , thư viện hỗ trợ tốt cho unicode , grapheme , hoạt động chuỗi , bản địa hóa và nhiều hơn nữa, xem bên dưới.
Tôi sẽ trích dẫn một số thông tin về hỗ trợ unicode trong PHP bằng các slide của Elizabeth Smith tại PHPBenelux'14
INTL
Tốt:
- Bao quanh thư viện ICU
- Ngôn ngữ chuẩn hóa, đặt ngôn ngữ cho mỗi tập lệnh
- Định dạng số
- Định dạng tiền tệ
- Định dạng tin nhắn (thay thế gettext)
- Lịch, ngày tháng, múi giờ và thời gian
- Người chuyển ngữ
- Kẻ lừa đảo
- Gói tài nguyên
- Người chuyển đổi
- IDN hỗ trợ
- Graphemes
- Đối chiếu
- Trình lặp lại
Xấu:
- Không hỗ trợ zend_multibite
- Không hỗ trợ chuyển đổi đầu ra đầu vào HTTP
- Không hỗ trợ quá tải chức năng
mb_string
- Bật hỗ trợ zend_multibyte
- Hỗ trợ mã hóa HTTP vào / ra trong suốt
- Cung cấp một số trình bao bọc cho những người yêu thích, chẳng hạn như strtoupper
ICONV
- Chính để chuyển đổi bộ ký tự
- Trình xử lý bộ đệm đầu ra
- chức năng mã hóa mime
- sự hoán cải
- một số trình trợ giúp chuỗi (len, substr, strpos, strrpos)
- Bộ lọc luồng
stream_filter_append($fp, 'convert.iconv.ISO-2022-JP/EUC-JP')
DỮ LIỆU
- mysql: Bộ mã và đối chiếu trên bảng và trên kết nối (không phải đối chiếu). Cũng không sử dụng mysql - msqli hoặc PDO
- postgresql: pg_set_client_encoding
- sqlite (3): Đảm bảo rằng nó được biên dịch với hỗ trợ unicode và intl
Một số Gotchas khác
- Bạn không thể sử dụng tên tệp unicode với PHP và windows trừ khi bạn sử dụng phần mở rộng phần thứ 3.
- Gửi mọi thứ trong ASCII nếu bạn đang sử dụng lệnh thực thi, proc_open và các lệnh gọi dòng lệnh khác
- Văn bản thuần túy không phải là văn bản thuần túy, các tệp có mã hóa
- Bạn có thể chuyển đổi tệp nhanh chóng với bộ lọc iconv
Tôi sẽ cập nhật câu trả lời này trong trường hợp mọi thứ thay đổi các tính năng được thêm vào, v.v.
Điều duy nhất tôi muốn thêm vào những câu trả lời tuyệt vời này là nhấn mạnh vào việc lưu các tệp của bạn ở dạng mã hóa utf8, tôi nhận thấy rằng các trình duyệt chấp nhận thuộc tính này thay vì đặt utf8 làm mã hóa mã của bạn. Bất kỳ trình soạn thảo văn bản tốt nào cũng sẽ hiển thị cho bạn điều này, ví dụ Notepad ++ có một tùy chọn menu để bao bọc tệp, nó hiển thị cho bạn bảng mã hiện tại và cho phép bạn thay đổi nó. Đối với tất cả các tệp php của tôi, tôi sử dụng utf8 mà không có BOM.
Cách đây không lâu, tôi có người yêu cầu tôi thêm hỗ trợ utf8 cho ứng dụng php / mysql do người khác thiết kế, tôi nhận thấy rằng tất cả các tệp đều được mã hóa bằng ANSI, vì vậy tôi phải sử dụng ICONV để chuyển đổi tất cả các tệp, thay đổi bảng cơ sở dữ liệu để sử dụng utf8 charset và utf8_general_ci collate, thêm 'SET NAMES utf8' vào lớp trừu tượng cơ sở dữ liệu sau kết nối (nếu sử dụng 5.3.6 hoặc cũ hơn, bạn phải sử dụng charset = utf8 trong chuỗi kết nối) và thay đổi các hàm chuỗi để sử dụng php multibyte các hàm chuỗi tương đương.
Gần đây tôi đã phát hiện ra rằng việc sử dụng strtolower()
có thể gây ra sự cố trong đó dữ liệu bị cắt ngắn sau một ký tự đặc biệt.
Giải pháp là sử dụng
mb_strtolower($string, 'UTF-8');
mb_ sử dụng MultiByte. Nó hỗ trợ nhiều ký tự hơn nhưng nói chung là chậm hơn một chút.
Tôi vừa trải qua vấn đề tương tự và tìm thấy một giải pháp tốt trong sách hướng dẫn sử dụng PHP.
Tôi đã thay đổi tất cả mã hóa tệp của mình thành UTF8, sau đó là mã hóa mặc định trên kết nối của tôi. Điều này đã giải quyết tất cả các vấn đề.
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());
}
Xem nguồn
Trong PHP, bạn sẽ cần sử dụng các hàm multibyte hoặc bật mbstring.func_overload . Bằng cách đó, những thứ như strlen sẽ hoạt động nếu bạn có các ký tự chiếm nhiều hơn một byte.
Bạn cũng sẽ cần xác định bộ ký tự cho các câu trả lời của mình. Bạn có thể sử dụng AddDefaultCharset, như trên hoặc viết mã PHP trả về tiêu đề. (Hoặc bạn có thể thêm thẻ META vào tài liệu HTML của mình.)
Hỗ trợ Unicode trong PHP vẫn còn là một mớ hỗn độn. Mặc dù nó có khả năng chuyển đổi một chuỗi ISO8859 (mà nó sử dụng nội bộ) thành utf8, nhưng nó thiếu khả năng làm việc với các chuỗi unicode nguyên bản, có nghĩa là tất cả các hàm xử lý chuỗi sẽ làm hỏng và làm hỏng các chuỗi của bạn. Vì vậy, bạn phải sử dụng một thư viện riêng để được hỗ trợ utf8 thích hợp hoặc tự viết lại tất cả các hàm xử lý chuỗi.
Phần dễ dàng chỉ là chỉ định bộ ký tự trong tiêu đề HTTP và trong cơ sở dữ liệu, nhưng không có vấn đề gì trong số đó nếu mã PHP của bạn không xuất ra UTF8 hợp lệ. Đó là phần khó và PHP hầu như không giúp bạn gì ở đó. (Tôi nghĩ PHP6 được cho là sẽ khắc phục điều tồi tệ nhất của điều này, nhưng điều đó vẫn còn một thời gian nữa)
Nếu bạn muốn máy chủ MySQL quyết định bộ ký tự, chứ không phải PHP làm máy khách (hành vi cũ; ưu tiên, theo ý kiến của tôi), hãy thử thêm skip-character-set-client-handshake
vào my.cnf
, dưới [mysqld]
và khởi động lại của bạn mysql
.
Điều này có thể gây ra sự cố trong trường hợp bạn đang sử dụng bất kỳ thứ gì khác ngoài UTF8.
Câu trả lời hàng đầu là tuyệt vời. Đây là những gì tôi phải làm khi thiết lập debian / php / mysql thông thường:
// 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
đó là tất cả !
nếu bạn muốn một giải pháp mysql, tôi đã gặp vấn đề tương tự với 2 trong số các dự án của mình, sau khi di chuyển máy chủ. Sau khi tìm kiếm và thử rất nhiều giải pháp, tôi đã tìm ra giải pháp này / không có gì trước khi giải pháp này hoạt động):
mysqli_set_charset($con,"utf8");
Sau khi thêm dòng này vào tệp cấu hình của tôi, mọi thứ đều hoạt động tốt!
Tôi đã tìm thấy giải pháp này https://www.w3schools.com/PHP/func_mysqli_set_charset.asp khi tôi đang tìm cách giải một đoạn chèn từ truy vấn html
chúc may mắn!
Chỉ cần một lưu ý:
Bạn đang phải đối mặt với vấn đề các ký tự không phải latin của bạn hiển thị như ?????????
, bạn đã đặt một câu hỏi và nó được đóng lại với tham chiếu đến câu hỏi chính tắc này, bạn đã thử mọi thứ và bất kể bạn làm gì bạn vẫn nhận được ??????????
từ đó MySQL
.
Điều đó chủ yếu là do bạn đang kiểm tra dữ liệu cũ đã được chèn vào cơ sở dữ liệu bằng cách sử dụng sai bộ mã và được chuyển đổi và lưu trữ thành các ký tự dấu chấm hỏi ?
. Điều đó có nghĩa là bạn đã mất văn bản gốc mãi mãi và bất kể bạn cố gắng làm gì, bạn sẽ nhận được ???????
.
áp dụng những gì bạn đã học được từ câu trả lời của câu hỏi này trên một dữ liệu mới có thể giải quyết vấn đề của bạn.
trong connection.php: mysqli_set_charset ($ con, “utf8”); và trong đối chiếu sql utf = 8