找回密碼
 註冊

QQ登錄

只需一步,快速開始

搜索
熱搜: 活動 交友 discuz
查看: 5816|回復: 18

让 Discuz! 论坛支持全部 Unicode 字符的方法

[複製鏈接]
發表於 2007-9-12 10:37:19 | 顯示全部樓層 |閱讀模式

首先,需要说明的是,Discuz! + MySQL 的方式之所以无法支持扩展区的汉字,并不是 Discuz! 的原因,而是 MySQL 的问题,当 MySQL 遇到扩展区的汉字(5 个字节的 utf8 编码)时,MySQL 的校验机制会认为是错误字节而将之截断。

 

换句话说,如果将来某一天 MySQL 修正了这个问题,那么什么都不要改,直接安装 utf8 的 Discuz! 论坛就行了。也就是说,现在改了以后,将来还要再想法改回来,不能直接升级,切记!

 

解决这个 Bug 的关键就是让 MySQL 的错误纠正机制不起作用,那么怎么办才能不起作用呢?让系统认为是单字节编码,从 \x01~\xFF 全部用上的单字节编码,这样就不会遇到所谓的“错误”字节了。而 latin1 恰好是这样的编码,所以原理就是这样的:在传输和显示时都是以 utf8 编码的格式来进行,但是在存储时则以 latin1 的编码格式来存储,这样就不会有字符丢失的现象了。

 

不过这个方案有个缺点:直接查看数据库就会发现汉字全部都是拉丁乱码了,假如要直接修改数据库的数值的话就有些困难了。但是在 Discuz! 论坛网页上则都是正确的 utf8 字符了。

 

那么改如何以 latin1 的编码格式来存储呢?这个稍后在说,转成 latin1 编码后,虽然之后存储的文字都是可以正常存取的,但是原先在数据库中的数据就会变成“?????”,一旦变成“?????”,那是绝对没办法恢复的,切记!

 

所以首先要做的是:将原来的数据转换成拉丁乱码,存储进数据库。

 

1、运行以下命令导出数据库脚本(注意:不是备份数据库)

 

mysqldump -uroot -p discuz >C:\utf8.sql

 

mysqldump:是安装 MySQL 之后自带的一个导出工具,在命令行下运行。

-uroot:这个是导出使用的帐号,一般 root 是具有管理员权限的,紧跟着参数 -u 就行了,如果要使用其他帐号,就写成“-uusername”,“-u”和“username”之间不要加空格。

-p:提示输入密码。

discuz:要导出的数据库名称,这个按实际情况修改。

>C:\utf8.sql:导出的路径,请按实际情况修改。

 

运行后,会提示输入密码,然后就会自动导出到指定文件了。

 

2、用 EmEditor 打开 C:\utf8.sql 文件,先检查一遍是不是正确的汉字,右下角是不是显示“UTF-8 不带签名”。这一点很重要,请务必确认。

 

3、双击 EmEditor 右下角的“UTF-8 不带签名”,然后编码选择“西欧”(一般是最后一个,请注意不要选错,不然导入后会无法正确显示汉字的。)

 

4、全选-复制,然后新建一个文件,粘贴。

 

5、保存。在保存时,建议换个文件另存,不要把原来的文件替换;然后“编码”下拉菜单选择“UTF-8”,取消“添加一个 Unicode 签名(BOM)”,然后保存。

 

6、打开保存的文件进行确认(这里假设保存为 latin1.sql 文件),注意看右下角是不是显示“UTF-8 不带签名”。如果不是,请重复步骤 2、3、4、5。

 

7、上述步骤确认无误后,就可以执行导入的命令了

 

mysql -uroot -p discuz <C:\latin1.sql

 

导入命令和导出命令的参数类似,请自行修改数据库名“discuz”,和导入的脚本路径“C:\latin1.sql”。

 

导入成功后,现在查看数据库,应该发现里面的汉字都是乱码了(如果不是乱码请重复步骤 2、3、4、5)。接下来就是修改程序代码,使其可以正常显示 utf8 字符,而不是显示拉丁乱码。

 

8、打开 Discuz! 论坛路径下的 \include\db_mysql.class.php 文件,在第 35 行有如下代码:

 

@mysql_query("SET character_set_connection=$dbcharset, character_set_results=$dbcharset, character_set_client=binary", $this->link);

 

将其改为:


@mysql_query("SET character_set_connection=$dbcharset, character_set_results=$dbcharset, character_set_client=binary, character_set_database=latin1, character_set_system=$dbcharset", $this->link);

 

保存(不用改编码)。

 

然后打开论坛看看,应该什么都没变吧。但是其实可以说现在整个论坛已经脱胎换骨了,随便输入一个扩展区的汉字看看呢,成功了吧。

 

×××××××××××××××××××××××××××××××××××××××××××××××××××××

 

啰啰嗦嗦讲了这么多,下面再来说说如何恢复成原来的状态吧。

 

1、导出数据库脚本:参见上面第一步。(假设导出的文件名为 latin1.sql)

2、用 EmEditor 打开 latin1.sql。

3、新建一个空白文件,另存一下(在 EmEditor 中新建的空白文件似乎无法保存),编码选择“西欧”(假设新建的文件是 utf8.sql)。

4、确认 utf8.sql 文件的编码是“西欧”。

5、将 latin1.sql 中的文字全部复制到 utf8.sql 中去,然后保存。

6、双击 utf8.sql 文件窗口右下角的“西欧”,将编码改成“UTF-8”,确认中文是否显示正常,如果不正常,则重复步骤 2、3、4、5。

7、将 utf8.sql 导入数据库。

8、打开 Discuz! 论坛路径下的 \include\db_mysql.class.php 文件,在第 35 行有如下代码:

 

@mysql_query("SET character_set_connection=$dbcharset, character_set_results=$dbcharset, character_set_client=binary, character_set_database=latin1, character_set_system=$dbcharset", $this->link);

 

将其恢复为:


@mysql_query("SET character_set_connection=$dbcharset, character_set_results=$dbcharset, character_set_client=binary", $this->link);

 

至此就大功告成了。

[ 本帖最後由 tantiancai 於 2007-9-12 12:01 編輯 ]
發表於 2007-9-12 12:13:53 | 顯示全部樓層

 

 

方法不太可取...................破壞數據

 樓主| 發表於 2007-9-12 12:22:46 | 顯示全部樓層
数据是完整的,只不过显示为拉丁乱码而已……
發表於 2007-9-15 00:19:13 | 顯示全部樓層
好似漢典網都係Discuz,但係佢可以顯示unicode的漢字我哋論壇可以向佢取下經!http://www.zdic.net/
 樓主| 發表於 2007-12-24 09:14:39 | 顯示全部樓層
据「汉典」管理员的描述,将数据库导入到本地后,发现里面的汉字全部是乱码,所以猜测可能是直接设置 MySQL 的默认编码,这样就不用修改程序了。但是要显示扩展汉字还是必须以 Latin1 编码的方式存到数据库中才行(原因上面已经分析过了)。

另外,经过详细测试,发现只要把 \include\db_mysql.class.php 文件中的以下代码删除即可:

@mysql_query("SET character_set_connection=$dbcharset, character_set_results=$dbcharset, character_set_client=binary", $this->link);


换句话说,就是不设置和数据库的连接编码,这样 MySQL 似乎默认就会以 Latin1 编码的方式存储了。

***************************************************

至于 GBK(Big5)版的 Discuz! 论坛显示扩展字符的方法可以参考:

http://www.pkucn.com/viewthread.php?tid=176085&page=1#pid1218157593

PS:感觉这个更安全,不用改数据库的,而且导出数据库是正常的中文,不是乱码,对于扩展字符,则是以 &#XXX; 的形式来存储的。
[ 本帖最後由 tantiancai 於 2007-12-25 17:21 編輯 ]
 樓主| 發表於 2007-12-24 21:57:56 | 顯示全部樓層
似乎确实有不用改程序显示扩展字符的方法,详请请见:http://www.pkucn.com/viewthread.php?tid=212890&page=1#pid1218324615

管理员可以尝试联系 yrr100,寻求具体的解决方案。
發表於 2007-12-24 22:39:00 | 顯示全部樓層

多謝提醒,但那還是需要修改discuz的代碼,放入數據庫的時候做encode,取出的時候做decode吧。

 樓主| 發表於 2007-12-25 17:20:23 | 顯示全部樓層
对,因为 MySQL 本身有 Bug,所以一定要改程序才行。管理员或许可以尝试将论坛转换到 Big5 版的 Discuz! 平台,然后只要改一个字的源代码就能完全支持 Unicode 了,不涉及数据库的编码转换的。应该是目前为止最安全的解决方案了。
發表於 2008-1-7 01:22:34 | 顯示全部樓層

原帖由 tantiancai 於 2007-12-25 17:20 發表 对,因为 MySQL 本身有 Bug,所以一定要改程序才行。管理员或许可以尝试将论坛转换到 Big5 版的 Discuz! 平台,然后只要改一个字的源代码就能完全支持 Unicode 了,不涉及数据库的编码转换的。应该是目前为止最安全的解决方 ...

 

你説的方法應該可行,但是需要把論壇數據庫裏面原來的unicode的內容轉換成GBK或者Big5,這樣改成GBK或者Big5論壇程序,前後的數據庫內容才一致。

 樓主| 發表於 2008-1-8 12:25:45 | 顯示全部樓層

要让论坛支持扩展字符除了对数据库做手脚之外,还有一个方法,那就是用 HTML 转义字符(HTML 实体,HTML Entity)来实现。


HTML 转义字符的含义和其他程序语言的转义字符的含义一致,都是通过若干普通字符的组合,来表示特殊字符。


比如:,在 HTML 转义字符中可以写成:&#x4EBA;,其代码是十六进制 Unicode 编码。


要实现转义字符的正常显示,首先要修改 Discuz! 论坛 /include/global.func.php 文件的第 187 行:


$string = preg_replace('/&amp;((#(\d{3,5}|x[a-fA-F0-9]{4})|[a-zA-Z][a-z0-9]{2,5});)/', '&\\1',


将代码中的 5 改成 7,4 改成 4,6,这样就能支持全部 Unicode 字符的显示了。

[ 本帖最後由 tantiancai 於 2008-1-14 14:52 編輯 ]
 樓主| 發表於 2008-1-8 12:48:30 | 顯示全部樓層

这样改了以后,理论上论坛就支持扩展字符了,可是问题是:一般人不太可能发帖的时候还去查某个扩展字符的 Unicode 编码,并写成转义字符的形式吧。这样就需要对 Discuz! 论坛程序本身再次进行修改,在用户发帖时通过 Javascript 脚本代码,自动将扩展字符转换成 HTML 转义字符。

 

打开 /include/javascript/post_editor.js 文件,在其中添加自动转换的脚本代码:

 

function toentities()
{
 if(wysiwyg)
 {
  editdoc.body.innerHTML = TextToEntities(getEditorContents(), true);
 }
 else
 {
  editdoc.value = TextToEntities(getEditorContents(), false);
 }
}

function TextToEntities(str, andEntity)
{
 var strFull = new Array();
 var intLowChar;
 var strEntity;
 var strChar;
 var intChar;

 for (var i = 0; i < str.length; i++)
 {
  strChar = str.charAt(i);
  intChar = str.charCodeAt(i);

  //CodePoint=((HighSurr-0xD800)/64+1)*0x10000+(HighSurr-0xD800) mod 64*1024+LowSurr-0xDC00
  if(intChar >= 0xD800 && intChar <= 0xDBFF)
  {
   intLowChar = str.charCodeAt(i + 1);
   intChar = ((intChar - 0xD800) / 64 + 1) * 0x10000 + (intChar - 0xD800) % 64 * 1024 + intLowChar - 0xDC00;
   i++;
   if(andEntity)
   {
    strEntity = "&amp;#" + intChar + ";";
   }
   else
   {
    strEntity = "&#" + intChar + ";";
   }
   strFull.push(strEntity);
  }
  else
  {
   strFull.push(strChar);
  }
 }
 return strFull.join("");
}

 

打开 templates/default/post_js.htm 文件(具体位置请按照实际使用的模板文件来修改),找到这样一行:

 

$('postform').onsubmit = function() {validate(this);if($('postsubmit').name != 'editsubmit') return false};

 

这个就是点击「发表」按钮后执行的脚本命令,可以在「validate(this);」这句话之前添加「toentities();」语句,执行转换函数,修改后的代码是这样的:

 

$('postform').onsubmit = function() {toentities();validate(this);if($('postsubmit').name != 'editsubmit') return false};

 

到此修改就结束了,重新打开浏览器试试看吧。

[ 本帖最後由 tantiancai 於 2008-1-8 12:52 編輯 ]
 樓主| 發表於 2008-1-8 14:05:12 | 顯示全部樓層

附件是将扩展字符转换成 HTML 转义字符的测试网页。

 

PS:这样一来虽然发帖是正常了,可是其他要输入文字的地方还是不可以,比如搜索、短消息等等,如果要一个个改的话,我觉得不如改数据库来得干脆呢。

本帖子中包含更多資源

您需要 登錄 才可以下載或查看,沒有賬號?註冊

×
 樓主| 發表於 2008-1-8 15:03:28 | 顯示全部樓層

刚刚发现,如果允许使用「Html 代码」的话,那么在「所见即所得模式」下会把转义字符直接显示出来,解决办法有两个:

1、关闭「Html 代码」;

2、切换到「Discuz! 代码模式」然后再发布。

發表於 2008-1-8 16:22:23 | 顯示全部樓層

看來支持unicode字符指日可待了!

 

 

[ 本帖最後由 鑊鑊金 於 2008-1-8 16:23 編輯 ]
 樓主| 發表於 2008-1-9 16:59:57 | 顯示全部樓層
通过查找短信、搜索等代码,我发现用的脚本文件并不通用,所以要达到通用的目的,只能把那段脚本加在 common.js 里面,这样才能保证通用性,至于之前的帖子嘛……先无视吧。 在 common.js 里面加入以下代码(之前如果已经在 post_editor.js 文件添加了代码的话请删除,当时没有考虑通用性的问题):
  1. function toentities()
  2. {
  3. if($("srchtxt")) // search by text
  4. $("srchtxt").value = TextToEntities($("srchtxt").value, false);
  5. if($("srchname")) // search by username
  6. $("srchname").value = TextToEntities($("srchname").value, false);
  7. if($("pm_textarea")) // pm
  8. $("pm_textarea").value = TextToEntities($("pm_textarea").value, false);
  9. if($("subject")) // thread subject
  10. $("subject").value = TextToEntities($("subject").value, false);
  11. if($("message")) // thread content
  12. $("message").value = TextToEntities($("message").value, false);
  13. if($("tags")) // tags
  14. $("tags").value = TextToEntities($("tags").value, false);
  15. if(document.getElementsByName("polloptions")[0]) //polloptions
  16. document.getElementsByName("polloptions")[0].value = TextToEntities(document.getElementsByName("polloptions")[0].value, false);
  17. if($("activityclass")) // activityclass
  18. $("activityclass").value = TextToEntities($("activityclass").value, false);
  19. if($("activityplace")) // activityplace
  20. $("activityplace").value = TextToEntities($("activityplace").value, false);
  21. if($("activitycity")) // activitycity
  22. $("activitycity").value = TextToEntities($("activitycity").value, false);
  23. if($("counterdesc")) // counter sellers
  24. $("counterdesc").value = TextToEntities($("counterdesc").value, false);
  25. if($("aboutcounter")) // about counter
  26. $("aboutcounter").value = TextToEntities($("aboutcounter").value, false);
  27. if($("item_name")) // sold item's name
  28. $("item_name").value = TextToEntities($("item_name").value, false);
  29. if($("item_locus")) // sold item's location
  30. $("item_locus").value = TextToEntities($("item_locus").value, false);
  31. if($("affirmpoint")) // affirm point
  32. $("affirmpoint").value = TextToEntities($("affirmpoint").value, false);
  33. if($("negapoint")) // nega point
  34. $("negapoint").value = TextToEntities($("negapoint").value, false);
  35. if($("umpire")) // umpire
  36. $("umpire").value = TextToEntities($("umpire").value, false);
  37. if(typeof(editdoc) != "undefined") // thread
  38. {
  39. if(wysiwyg)
  40. {
  41. editdoc.body.innerHTML = TextToEntities(getEditorContents(), true);
  42. }
  43. else
  44. {
  45. editdoc.value = TextToEntities(getEditorContents(), false);
  46. }
  47. }
  48. }
  49. function TextToEntities(str, andEntity)
  50. {
  51. var strFull = new Array();
  52. var intLowChar;
  53. var strEntity;
  54. var strChar;
  55. var intChar;
  56. for (var i = 0; i < str.length; i++)
  57. {
  58. strChar = str.charAt(i);
  59. intChar = str.charCodeAt(i);
  60. //CodePoint=((HighSurr-0xD800)/64+1)*0x10000+(HighSurr-0xD800) mod 64*1024+LowSurr-0xDC00
  61. if(intChar >= 0xD800 && intChar <= 0xDBFF)
  62. {
  63. intLowChar = str.charCodeAt(i + 1);
  64. intChar = ((intChar - 0xD800) / 64 + 1) * 0x10000 + (intChar - 0xD800) % 64 * 1024 + intLowChar - 0xDC00;
  65. i++;
  66. if(andEntity)
  67. {
  68. strEntity = "&amp;#" + intChar + ";";
  69. }
  70. else
  71. {
  72. strEntity = "&#" + intChar + ";";
  73. }
  74. strFull.push(strEntity);
  75. }
  76. else
  77. {
  78. strFull.push(strChar);
  79. }
  80. }
  81. return strFull.join("");
  82. }
複製代碼
然后是 post.js 文件,在 function ctlent(event) 的 if 语句后面加上 toentities(); 函数,修改后的代码如下:
  1. function ctlent(event) {
  2. if(postSubmited == false && (event.ctrlKey && event.keyCode == 13) || (event.altKey && event.keyCode == 83) && $('postsubmit')) {
  3. toentities();
  4. ……
複製代碼
接着改以下文件(第一行为原来的代码,第二行为修改后的代码):
  1. // global.func.php
  2. $string = preg_replace('/&amp;((#(\d{3,5}|x[a-fA-F0-9]{4})|[a-zA-Z][a-z0-9]{2,5});)/', '&\\1',
  3. $string = preg_replace('/&amp;((#(\d{3,6}|x[a-fA-F0-9]{5})|[a-zA-Z][a-z0-9]{2,5});)/', '&\\1',
  4. // post_js.htm
  5. $('postform').onsubmit = function() {validate(this);if($('postsubmit').name != 'editsubmit') return false};
  6. $('postform').onsubmit = function() {toentities();validate(this);if($('postsubmit').name != 'editsubmit') return false};
  7. // pm_send.htm
  8. // search.htm
  9. // pm_search.htm
  10. // forumdisplay.htm
  11. // viewthread.htm
  12. // viewthread_fastreply.htm
  13. // post_editpost_activity.htm
複製代碼
这些代码大多都是加入了提交时的转换函数,这样在提交时就自动会转化了。
 樓主| 發表於 2008-1-9 17:05:20 | 顯示全部樓層

晕,代码全乱掉了……我把上面帖子的内容以文本文件的形式传上来吧。

 

顺便再来测试一下:𪚥

[ 本帖最後由 tantiancai 於 2008-4-10 15:44 編輯 ]

本帖子中包含更多資源

您需要 登錄 才可以下載或查看,沒有賬號?註冊

×
 樓主| 發表於 2008-1-9 17:10:54 | 顯示全部樓層

这样改了以后就「短消息」(短消息搜索后显示有问题,不过搜索没问题)「搜索」「发帖」都支持扩展字符了。

 

不过没办法注册成扩展字符的用户名,这个是系统限制,不是改脚本就可以解决的了。

 

另外,上传附件的「描述」也不支持扩展字符。

 樓主| 發表於 2008-11-30 12:45:17 | 顯示全部樓層

最新消息,升级到MySql 6.0.4以上版本就能不修改任何东西,直接支持扩展字符了。

 

下载地址:

http://dev.mysql.com/downloads/mysql/6.0.html

 

介绍:

http://dev.mysql.com/doc/refman/6.0/en/charset-unicode.html

發表於 2008-11-30 13:42:59 | 顯示全部樓層

原帖由 tantiancai 於 2008-11-30 12:45 發表 最新消息,升级到MySql 6.0.4以上版本就能不修改任何东西,直接支持扩展字符了。   下载地址: http://dev.mysql.com/downloads/mysql/6.0.html   介绍: http://dev.mysql.com/doc/refman/6.0/en/charset-unicode ...

 

Thank you, tantiancai !

您需要登錄後才可以回帖 登錄 | 註冊

本版積分規則

Archiver|手機版|粵語協會

GMT+8, 2024-5-16 04:17 , Processed in 0.088241 second(s), 21 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回復 返回頂部 返回列表