技术开发 频道

小议MySQL中的字符集处理

  【IT168技术文档】MySQL数据库在字符集的处理上,可以说是相当灵活。以下是我在实际工作中关于字符集的设置与处理方面的一个总结。

  一、显示字符集相关的变量值

  mysql> show variables like 'chara%';

  +--------------------------+-------------------------------------------+

  | Variable_name | Value |

  +--------------------------+-------------------------------------------+

  | character_set_client | latin1 |

  | character_set_connection | latin1 |

  | character_set_database | latin1 |

  | character_set_results | latin1 |

  | character_set_server | latin1 |

  | character_set_system | utf8 |

  | character_sets_dir | d:\mysql-5.0.9-beta-win32\share\charsets/ |

  +--------------------------+-------------------------------------------+

  7 rows in set (0.02 sec)

  mysql> show variables like 'collation%';

  +----------------------+-------------------+

  | Variable_name | Value |

  +----------------------+-------------------+

  | collation_connection | latin1_swedish_ci |

  | collation_database | latin1_swedish_ci |

  | collation_server | latin1_swedish_ci |

  +----------------------+-------------------+

  3 rows in set (0.00 sec)

  需要说明的是: collation是与排序相关的字符集变量, 上述变量值是mysql中缺省的字符集变量。

  二、字符集相关各变量的意义

  character_set_client: 客户端字符集(应用程序客户端使用的字符集),实际上它与当前会话相关。

  character_set_connection: 连接字符集,指的是mysql网络传输中使用的字符集,也与会话相关。

  character_set_database: 指的数据库表中物理存储使用的字符集。

  character_set_results: 指的是使用sql查询处理以后返回结果使用的字符集。

  character_set_server:指的是mysql数据库服务器端使用的字符集。

  character_set_system:指的是数据库中存储元信息使用的字符集。

  collation_connection:意义可以类推

  三、字符集的修改

  我们可以对单个变量进行修改,如

  set character_set_client=gbk

  也可以使用set names 来同时修改几个变量, 如

  set names 'gbk' 可一下子全部修改字符集

  等价于:

  SET character_set_client = x;

  SET character_set_results = x;

  SET character_set_connection = x;

  值得注意的是:collation_connection变量缺省下与character_set_connection是相关联的,即

  collation_connection取决于character_set_connection的值。

  如:

  mysql> set names 'gbk';

  Query OK, 0 rows affected (0.08 sec)

  mysql> show variables like 'chara%';

  +--------------------------+-------------------------------------------+

  | Variable_name | Value |

  +--------------------------+-------------------------------------------+

  | character_set_client | gbk |

  | character_set_connection | gbk |

  | character_set_database | latin1 |

  | character_set_results | gbk |

  | character_set_server | latin1 |

  | character_set_system | utf8 |

  | character_sets_dir | d:\mysql-5.0.9-beta-win32\share\charsets/ |

  +--------------------------+-------------------------------------------+

  7 rows in set (0.00 sec)

  mysql> show variables like 'collation%';

  +----------------------+-------------------+

  | Variable_name | Value |

  +----------------------+-------------------+

  | collation_connection | gbk_chinese_ci |

  | collation_database | latin1_swedish_ci |

  | collation_server | latin1_swedish_ci |

  +----------------------+-------------------+

  3 rows in set (0.00 sec)

  我们可以看到 set names 'gbk'改变了character_set_client, character_set_connection, character_set_results, collation_connection的值.

  如果要单独指定collation_name,可以使用

  SET NAMES 'charset_name' COLLATE 'collation_name'

  另一种修改多个变量的命令是:

  SET CHARACTER SET x 等价于:

  SET character_set_client = x;

  SET character_set_results = x;

  SET collation_connection = @@collation_database;

  你可以自己体验一下该命令带来的字符集变量值的变动。

  那么character_set_server, character_set_database的值怎么修改呢?直接在命令行里修改是不起作用的。

  最好的方式是在配置文件my.ini中直接修改,在[mysqld]项下边添加:

  default_character_set=GBK

  当然,你如果想尝试,使用mysqld-nt --default_character_set=GBK来启动数据库也未尝不可,但我并不推荐这么做。

  my.ini文件的搜索顺序是:

  在版本4.1.5之前,必须在c:\my.cnf or c:\Windows\my.in下边,这个有待检验。

  在后续版本里,my.ini缺省就在mysql的安装目录下边。

  强调一点:

  mysql --default-character-set=gbk -u root

  或

  mysql --defaults-file=c:\my.ini -u root

  在my.ini中添加如下内容

  [mysql] or [client]

  default-character-set=gbk

  或

  mysql -u root

  再执行set names 'gbk'

  这三者效果完全一样。

  四、乱码的产生

  乱码的产生主要是由于字符集变量设置的不一致造成的。

  操作系统有一个在显示字符时要使用的locale变量,linux下使用locale命令可以看到。

  windows下使用chcp可以看到,gbk对应的code page应该是936。

  如果操作系统安装了当前字符集与目标字符集的转换表,则能正确显示目标字符集中的字符,否则就会出现乱码。

  latin1对应的是iso8859_1字符集,默认情况下,可以与gbk相互转换,至少在操作系统一级是这样。

  五.乱码的避免

  最好让上述9个字符集变量值保持一致,或者至少“兼容”,同时也要考虑到OS中locale的值。

  当然:character_set_system例外,它是存储和表示元信息使用的字符集,一般都是ascii串,使用utf8和使用latin1基本一样,但是,如果使用中文,可能就另当别论了。下边说的全部变量是指除了character_set_system以外的其它变量。

  这里推荐三个方案:

  1. 全部使用latin1

  但是在java程序中,它担着一定的风险,即在入库之前,需要将字符串从gbk转换到iso8859_1,出库以后,获取结果时,再从iso8859_1转到gbk.

  否则会出现乱码。

  这种方式比较适合于C代码,显示依赖于操作系统的locale.一般都不用转换。

  2. 全中文支持,全部设置成gbk.

  方法:

  在my.ini中修改添加:(这个是必须的)

  [mysqld]

  default-character-set=gbk

  在java程序里边使用"jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=GBK"这样的url,表明使用GBK进行编码。

  3. utf8字符集支持.

  方法:

  在my.ini中修改添加:

  [mysqld]

  default-character-set=utf8

  在java程序里边使用"jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8"这样的url,表明使用GBK进行编码。

  注意utf8与UTF-8的分别.

  utf8的好处是java虚拟机可以自动将它与gbk进行转换,因而显示都不会有乱码。可是在控制台下(cmd),显示就有问题了。

  六.使用java代码显示字符集变量及测试字符集的显示

  因为只是作测试用,所以没加修饰。测试时,只需要按照上述三个方法修改字符集即可。 

  1 import java.sql.*;
  2
  3 /** *//**
  4 *
  5 Title:
  6
  7 *
  8 *
  9 Description:
10
11 *
12 *
13 Copyright: Copyright (c) 2006
14
15 *
16 *
17 Company:
18
19 *
20 * @author not attributable
21 * @version 1.0
22 */
23 public class TestCharset ...{
24   String username = "root";
25   String passwd = "";
26   Connection conn = null;
27   String charset = null;
28   public TestCharset() ...{
29   }
30
31   public void connect() throws SQLException, ClassNotFoundException ...{
32     Class.forName("com.mysql.jdbc.Driver");
33     String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8";
34     conn = DriverManager.getConnection(url, username, passwd);
35     charset = url.substring(url.lastIndexOf("=")+1);
36   }
37
38   public void getCharset() throws SQLException...{
39     Statement stmt = conn.createStatement();
40     System.out.println("=======show variables like 'chara%'========");
41     ResultSet rset = stmt.executeQuery("show variables like 'chara%'");
42     while (rset.next()) ...{
43       System.out.println(rset.getString(1) + " ------> " + rset.getString(2));
44     }
45     rset.close();
46     System.out.println("=======show variables like 'collation%'========");
47     rset = stmt.executeQuery("show variables like 'collation%'");
48     while (rset.next()) ...{
49       System.out.println(rset.getString(1) + " ------> " + rset.getString(2));
50     }
51     rset.close();  
52     stmt.close();
53   }
54
55   public void testGetValuesISO8859_1() throws Exception  ...{
56     Statement stmt = conn.createStatement();
57     try ...{
58       stmt.executeUpdate("drop table t12345");
59     } catch (Exception e) ...{
60     
61     }
62     stmt.executeUpdate("create table t12345(id int primary key, name varchar(32))");
63     String sz = new String("中文".getBytes("gbk"), "ISO8859_1");
64     stmt.executeUpdate("insert into t12345 values(1, '" + sz + "')");
65     ResultSet rset = stmt.executeQuery("select * from t12345");
66     rset.next();
67     System.out.println("测试中文值: " + new String(rset.getString(2).getBytes("ISO8859_1"), "GBK"));
68     rset.close();
69   
70     stmt.close();
71   }
72   public void testGetValuesGBK() throws Exception  ...{
73     Statement stmt = conn.createStatement();
74     try ...{
75       stmt.executeUpdate("drop table t12345");
76     } catch (Exception e) ...{
77
78     }
79     stmt.executeUpdate("create table t12345(id int primary key, name varchar(32))");
80     stmt.executeUpdate("insert into t12345 values(1, '中文')");
81     ResultSet rset = stmt.executeQuery("select * from t12345");
82     rset.next();
83     System.out.println("测试中文值: " + rset.getString(2));
84     rset.close();
85
86     stmt.close();
87   }
88   public void testGetValuesUTF8() throws Exception  ...{
89      Statement stmt = conn.createStatement();
90      try ...{
91        stmt.executeUpdate("drop table t12345");
92      } catch (Exception e) ...{
93
94      }
95      stmt.executeUpdate("create table t12345(id int primary key, name varchar(32))");
96      //String sz = new String("中文".getBytes("gbk"), "UTF8");
97      stmt.executeUpdate("insert into t12345 values(1, '中文')");
98      ResultSet rset = stmt.executeQuery("select * from t12345");
99      rset.next();
100      System.out.println("测试中文值: " + rset.getString(2));
101      rset.close();
102
103      stmt.close();
104   }
105   public void disconnect() throws SQLException...{
106     if (conn != null) conn.close();
107   }
108   public static void main(String[] args) ...{
109     TestCharset t = new TestCharset();
110     try ...{
111       t.connect();
112       t.getCharset();
113       if (t.charset.equals( "ISO8859_1" ))
114         t.testGetValuesISO8859_1();
115       else if (t.charset.equals("GBK"))
116         t.testGetValuesGBK();
117       else if (t.charset.equals("UTF-8"))
118         t.testGetValuesUTF8();
119     } catch (Exception e) ...{
120       //System.out.println(e.getMessage());
121       e.printStackTrace();
122     } finally ...{
123       try ...{
124         t.disconnect();
125       } catch (Exception e2) ...{
126       }
127     }
128   }
129 }
0
相关文章