我的数据库编码为UTF-8,数据连接为
con = DriverManager
.getConnection("jdbc:mysql://localhost/utftest?user=root&password=password&useUnicode=true&characterEncoding=GBK");
请问我用rs.getString(1);得到字符编码是什么,是不是与characterEncoding的值有关,谢谢!

解决方案 »

  1.   

    返回的是你characterEncoding的编码
      

  2.   

    我有一点不明白,我的输出没有加编码限制,res.setContentType("text/plain");采用的是默认的ISO-8859-1输出到客户端。
    如果数据库是UTF-8的,数据库连接是characterEncoding=GBK,为什么我用return new String(rs.getBytes(1), "ISO-8859-1");到客户端显示就是乱码,而数据库编码为GBK的话这样就没问题;同样,数据库是UTF-8,数据库连接是characterEncoding=UTF-8,return new String(rs.getBytes(1), "ISO-8859-1");得到仍是乱码,而把数据库编码改为GBK就又正常了,这不是与characterEncoding设置的值无关么。
    当然我要是用return new String(rs.getString(1).getBytes("GBK"),"ISO-8859-1");在上述条件下都可以得到正确值,还望大家告知!
      

  3.   

    是汉字乱码吗?
    我在linux下试过,mysql存汉字的时候本身就会乱码,在读取的时候最好来一次转换
    用byte解决
      

  4.   

    谢谢,问题是可以解决,我只要加上res.setContentType("text/plain; charset=GBK");就可以了,只是我想知道的是这种方式从数据库中取出的是什么编码,有人告诉我就是characterEncoding设置的值,但我的上述测试结果好像不能证明该结果,请问可以告诉我一个合理的解释么
      

  5.   

    你这种做法不是很合理,既然你数据库用UTF-8了,其他地方也应该都用UTF-8,可以更好的支持国际化,其实很多时候这种麻烦都是可以避免的
      

  6.   

    编码问题的关键是要理解概念:
    http://www.regexlab.com/zh/encoding.htm从数据库读取数据,到页面显示,中间的环节很多,问题不一定就出在搂主这里。因此,需要准确理解概念。
      

  7.   

    天哪,我只是想了解这个返回值的编码而已,实际的程序中我的MySQL编码为UTF-8,characterEncoding也为UTF-8,这不是看不出效果么,才在这两者之间两两用GBK和UTF
    -8四种组合来希望得到其返回值到底是与数据库编码有关还是与characterEncoding设置有关, 请大家不必太关心操作细节和结果,这些操作都是正确的,谢谢!
      

  8.   

    > 请问我用rs.getString(1);得到字符编码是什么,是不是与characterEncoding的值有关,谢谢!你这样问,本身就是不严格的。rs.getString() 得到的字符串,在 Java 程序里是一个 String 对象,在 JVM 运行时是用 UCS2 的形式保存在内存里的,不存在“得到字符编码是什么”的问题。换句话说,“字符串”本身是没有什么“编码方式”一说的,只有“字符串”跟“字节数组”进行相互转换的时候,才有“编码方式”一说。characterEncoding 参数的意义在于,JDBC Driver 在把数据从服务器端传送到客户端的过程中,网络上传输的字节流是按照什么样的“编码方式”对数据中的“字符串”进行拆解和包装的。在连接参数中设置这个 characterEncoding,要跟服务器上的相关设置相匹配,否则就会出现乱码。
      

  9.   

    那可不可以解释一下,输出为:
    res.setContentType("text/plain");
    PrintWriter pw = res.getWriter();
    pw.print(balance);
    pw.close();
    从数据库得到的结果操作为:
    ........
    con = DriverManager
    .getConnection("jdbc:mysql://localhost/utftest?user=root&password=password&useUnicode=true&characterEncoding=XXX");
    ........
    return new String(rs.getBytes(1), "ISO-8859-1");
    ........
    这里的数据库编码为GBK时,返回到客户端是正确的结果,而如果是UTF-8的话则是乱码,而不以XXX的值为变化,请问是为什么?
    当然用return new String(rs.getString(1).getBytes("GBK"),"ISO-8859-1");的话就与数据库编码是什么无关了
      

  10.   

    就楼主上贴提出的几个问题,我试着解释一下,有说得不对的地方大家指出。1. 不以XXX的值为变化characterEncoding=XXX 仅用于设置传输“字符串”时所用的编码方式,而当 return new String(rs.getBytes(1), "ISO-8859-1"); 时,是把字段里的数据当作“字节数组”来处理,所以跟 XXX 无关。
      

  11.   

    2. 这里的数据库编码为GBK时,返回到客户端是正确的结果,而如果是UTF-8的话则是乱码之所以“正确”,是因为 return new String(rs.getBytes(1), "ISO-8859-1") 能把数据库里保存的字符串按照 GBK 的编码方式拆成字节数组,然后把每个字节当作一个字符再拼装起来,然后把这样一个“不知所云”的字符串原样发送到客户端,而客户端并不知道这样一个复杂的过程,它只知道得到了一个字节流,而用 GBK 的编码方式去理解这个字节流的时候,“恰好”就是原来数据库里的字符串。同样,UTF-8 的“乱码”,也不是真的乱码。如果你的客户端按照 UTF-8 的方式去理解得到的这个字节流的时候,也会“恰好”就是原来数据库里的字符串。
      

  12.   

    3. 用return new String(rs.getString(1).getBytes("GBK"),"ISO-8859-1");的话就与数据库编码是什么无关了恐怕并不是“与数据库编码是什么无关了”,而是“characterEncoding=XXX 跟数据库编码匹配就可以了”。楼主不妨做做试验。分析一下细节:只要“characterEncoding=XXX 跟数据库编码匹配”,rs.getString(1) 就能得到一个“正确”的字符串,然后按照 GBK 拆解成字节数组,然后再按照 "ISO-8859-1" 拼装成一个“不知所云”的字符串……后面的事情跟前面 (2) 中所述一样。
      

  13.   

    首先非常感谢你对之前问题和现问题的回答!
    该问题的前两个解释很好,已弄明白,但针对第三个解释,本身我都没有想到以此发问,既然提到了,发现存在出入,你提到"恐怕并不是“与数据库编码是什么无关了”,而是“characterEncoding=XXX 跟数据库编码匹配就可以了”",我之前做过测试,还是这句话:
    con = DriverManager
    .getConnection("jdbc:mysql://localhost/utftest?user=root&password=password&useUnicode=true&characterEncoding=XXX");
    数据库的编码无论是GBK,UTF-8或是默认的Latin1,XXX的值为GBK或是UTF-8,用
    return new String(rs.getString(1).getBytes("GBK"),"ISO-8859-1");
    在客户端都能返回正确结果,并不似你说的那样,希望解惑!
      

  14.   

    【Sorry, 上贴作废,重发一遍】
    【hehe,刚发完就发现有问题了,但 CSDN 最多只能连发三贴,所以等了半天】
    3. 用return new String(rs.getString(1).getBytes("GBK"),"ISO-8859-1");的话就与数据库编码是什么无关了rs.getString(1) 总能得到一个“正确”的字符串,然后按照 GBK 拆解成字节数组,然后再按照 "ISO-8859-1" 拼装成一个“不知所云”的字符串……后面的事情跟前面 (2) 中所述一样。结论:既然 rs.getString() 总能得到“正确”的字符串,那么,根本就不需要转来转去的,直接 return rs.getString(1) 就好了。如果你用这个办法写到客户端后看到的是乱码,说明你的 PrintWriter 的 encoding 设得不对,想办法设成 GBK 就好了。
      

  15.   

    按我目前的理解,characterEncoding=XXX 只是“指导” JDBC Driver 在跟 Server 通信的时候,在进行网络传输的时候,对“字符串”采用什么样的编码方式。而作为应用程序,如果是用 rs.getString() 来获取结果,那么,JDBC Driver 总要负责把网络字节流包装成为 Java 意义上的“字符串”,就是一个 String 对象,所以,characterEncoding=XXX 设成什么并不重要了。但是,如果你调用的是 rs.getBytes(),那就全看 characterEncoding=XXX 了。
      

  16.   

    谢谢你费心了,其实也没啥,只是闲着无聊做个测试,我的第二个方法就是这样设置的,
    res.setContentType("text/plain; charset=GBK");
    PrintWriter pw = res.getWriter();
    pw.print(balance);
    pw.close();
    并且用rs.getString()作为返回值,
    而第一个没有加charset=GBK的多次试验情况下发现很多令我出乎意料的结果,由于对这块了解不够,才引发诸多疑问,谢谢你详细的解答!
    最后再问一句,rs.getString()总能得到“正确”的字符串,依据是什么
      

  17.   

    对不起,你的上述回复没能看懂,这样不是与你先前说的冲突了么1. 不以XXX的值为变化characterEncoding=XXX 仅用于设置传输“字符串”时所用的编码方式,而当 return new String(rs.getBytes(1), "ISO-8859-1"); 时,是把字段里的数据当作“字节数组”来处理,所以跟 XXX 无关。
      

  18.   

    我的理解是characterEncoding=XXX只与servlet数据保存的数据库有关,没有这句时,JDBC把UNICODE的字符转化为默认的ISO-8859-1进行传输,数据库接收并以自身的编码保存数据,加上了这句则是将UNICODE转化为指定的XXX进行传输,数据库再保存而已,而不应该以XXX此值来决定数据库的返回值的依据,不知是否正确。
      

  19.   

    en, 你说得对。看来我对 characterEncoding 的理解还是有问题。刚刚试验了一下,rs.getBytes() 得到的东西,只跟数据库的编码有关,永远是把字符串按照数据库编码拆解出来的字节数组,不管 characterEncoding 设成什么。
      

  20.   

    谢谢你的测试,最后想麻烦你,还是先头那句,“rs.getString()总能得到“正确”的字符串,依据是什么”?
      

  21.   

    我说 rs.getString()总能得到“正确”的字符串,一个是根据我实验的结果(也包括你前面描述的实验现象),另一个是根据我对 characterEncoding 的理解。
      

  22.   

    我对 characterEncoding 的最新理解   ^D^如果是服务器把字符串数据发送给客户端(比如 SELECT * FROM test),由服务器决定采用什么样的编码进行传输,同时会告知客户端,客户端就能正确解析;如果是客户端把字符串发送给服务器(比如 INSERT INTO test VALUES('中文') 或者 SELECT * FROM test WHERE name='中文'),由客户端决定采用什么样的编码方式进行传输,同时会告知服务器(通过 SET NAMES XXX),服务器端就能正确解析。前者就是“数据库编码”,后者就是“characterEncoding”。而具体选用什么编码才不会出现乱码,其实并不是唯一的,只要所选择的编码跟要表达的字符串是“兼容”的就行了。比如要传送“朱鎔基”,那么(无论是“数据库编码”还是“characterEncoding”)选择 GBK 或者 UTF-8 都是可以的,ISO8859_1 和 GB2312 就不行。
      

  23.   

    再补充一点题外话(这是以前遇到的现象,现在算是知道“所以然”了):characterEncoding 最好选择为跟“数据库编码”是一样的,否则在查询语句的 WHERE 条件中可能会出问题。比如数据库编码是 GBK,characterEncoding 是 UTF-8,那么 SELECT * FROM test WHERE name='中文' 就会报 Illegal mix of collations (gbk_chinese_ci,IMPLICIT) and (utf8_general_ci,COERCIBLE) for operation '='。
      

  24.   

    谢谢你的热心讨论,综上所述,还是预先对传入和传出数据进行编码控制比较方便、安全,下面是我的全部代码加注释,注释不对之处还望指正
    public class DBServlet extends HttpServlet { public int type; // 分别对应两种对中文数据的处理方式 private static final long serialVersionUID = 4208954264915608912L; /**
     * @函数功能 使用Servlet容器的默认编码(ISO-8859-1),在需要时应对相关数据进行解码处理
     */
    protected void doGet(HttpServletRequest req, HttpServletResponse res)
    throws ServletException, IOException {
    String acct = new String(req.getParameter("account").getBytes(
    "ISO-8859-1"), "GBK"); // 使用"ISO8859-1"字符集将接收的字符解码并存储到byte数组,再用GBK码将该数组构造为新的String
    String pwd = req.getParameter("password");
    String keyWord = req.getParameter("keyWord");
    type = 0; String balance = accountLookup(acct, pwd, keyWord, type); if (balance == null) {
    res.sendError(HttpServletResponse.SC_BAD_REQUEST,
    "Unable to locate account.");
    return;
    } res.setContentType("text/plain");
    PrintWriter pw = res.getWriter();
    pw.print(balance); // 按照平台的默认字符编码(ISO-8859-1)将字符串的字符转换为字节(编码),并完全以write(int)方法的方式写入这些字节
    pw.close();
    } /**
     * @函数功能 对输入请求和输出响应都预先加上字符编码,避免其中的字符编码转换
     */
    protected void doPost(HttpServletRequest req, HttpServletResponse res)
    throws ServletException, IOException {
    req.setCharacterEncoding("GBK"); // 设置输入编码格式,该方法需在读取数据前调用
    String acct = req.getParameter("account");
    String pwd = req.getParameter("password");
    String keyWord = req.getParameter("keyWord");
    type = 1; String balance = accountLookup(acct, pwd, keyWord, type); if (balance == null) {
    res.sendError(HttpServletResponse.SC_BAD_REQUEST,
    "Unable to locate account.");
    return;
    } res.setContentType("text/plain; charset=GBK"); // 设置输出编码格式,该方法需在使用PrintWriter流前调用
    PrintWriter pw = res.getWriter();
    pw.print(balance);
    pw.close();
    } /**
     * @param acct
     *            用户帐户
     * @param pwd
     *            用户密码
     * @param keyWord
     *            查询条件
     * @param type
     *            操作类型
     * @return
     * @函数功能 从数据库查询信息并返回结果
     */
    private String accountLookup(String acct, String pwd, String keyWord,
    int type) {
    Connection con = null; try {
    Class.forName("com.mysql.jdbc.Driver");
    // 这里"characterEncoding=UTF-8"是指将数据从unicode编码转换为指定UTF-8编码进行传输,否则用默认的"ISO-8859-1"的编码进行传输,
    // 之后在数据库操作中则是采用数据库指定的内部编码方式存储传来的数据
    con = DriverManager
    .getConnection("jdbc:mysql://localhost/utftest?user=root&password=password&useUnicode=true&characterEncoding=UTF-8");
    Statement stmt = con.createStatement();
    String sqlStr = "Select " + keyWord
    + " from userinfo where userName = '" + acct + "'"
    + " and userPwd = '" + pwd + "'";
    ResultSet rs = stmt.executeQuery(sqlStr); if (rs.next()) {
    if (type == 0) {
    // 用rs.getString()得到“正确”的字符串,再按照GBK拆解成字节数组(编码),然后用"ISO-8859-1"解码字节数组并生成字符串(解码)
    return new String(rs.getString(1).getBytes("GBK"),
    "ISO-8859-1");
    // rs.getBytes()得到的东西,只跟数据库的编码有关,得到的是把字符串按照数据库编码拆解出来的字节数组
    // return new String(rs.getBytes(1), "ISO-8859-1");
    } else {
    return rs.getString(1);
    }
    } else {
    return null;
    }
    } catch (Exception e) {
    return e.toString();
    }
    }}
      

  25.   

    en, 不错不错,这段测试程序写得很有点意思  8)顺便说一句,你的 type = 0 那种方式,就是经常见到有人用的一种办法,我管它叫“将错就错”。有人喜欢用 ISO8859-1 的方式传东西,觉得这样最可靠,甚至把数据库编码也设成 latin1,好像这样才不会损失信息。虽然这样勉强能实现“不出现乱码”,但实际上会隐藏很多问题(比如平台缺省编码跟预先假设的不一样、比如多国文字混排、比如数据库查询时的中文字符串匹配)。所以,只有把“所以然”搞清楚了,才能游刃有余地驾驭它  ^_^