问题

  用python的pymssql模块读取旧业务系统后台SQL Server 2000数据库展示数据为乱码

开发环境

  • 操作系统:windows 8
  • 数据库 MS SQL Server 2000,默认配置
  • python 2.7.6
  • pymssql 2.1.1
  • 开发工具:PyCharm 4.0

业务逻辑

  数据库的[rooms]表记录一些功能房间列表,与其他接口数据进行对比,然后输出对比结果。

  rooms表结构

CREATE TABLE [rooms] (
[id] [int] IDENTITY (1, 1) NOT NULL ,
[name] [varchar] (50) COLLATE Chinese_PRC_CI_AS NULL ,
PRIMARY KEY CLUSTERED ([id] ON [PRIMARY] ,
UNIQUE NONCLUSTERED ([des]) ON [PRIMARY]
) ON [PRIMARY]
GO

  模拟代码

# -*- coding: utf-8 -*-
import pymssql rooms=None
with pymssql.connect(host='192.168.1.100',database='builds',
user='sa',password='password',
# charset='utf8',
) as conn:
cur=conn.cursor()
sql="select id,name from rooms"
cur.execute(sql)
rooms=cur.fetchall()
if rooms and isinstance(rooms,(list,tuple)):
for room_id,room_name in rooms:
print "\t".join([str(room_id),room_name])

  在通用环境中运行代码,room_name变量列显示乱码

问题分析

  1. 调整连接字符集
    首先想到的解决办法是,指定pymssql.connect参数charset的字符集值,使得内外数据编码一致。
    依据,“默认情况下,SQL Server 2000使用ISO字符集(代码页1252)。这个字符集也叫ISO-8859-1 Latin1 或者ANSI字符集。它和Windows9x及Windows NT/2000操作系统相兼容,提供了与大多数语言最大兼容性。SQL Server2000中还包含代码页936(简体中文),该字符集包含对简体中文支持的字符”,将charset设置为gbk或cp936,更为合适。查看pymssql使用文档,发现官方没有给出此参数可接收的实例字符串。进行猜测性调试:
    <charset='gbk'>运行抛出异常:pymssql.OperationalError: (20017, 'DB-Lib error message 20017, severity 9:\nUnexpected EOF from the server\nDB-Lib error message 20002, severity 9:\nAdaptive Server connection failed\n')
    <charset='cp936'>调试模式下pymsql.connect无异常信息,但程序直接退出
    <charset='utf8'>运行正常,输出依然乱码;不指定此参数值时,程序使用默认值'UTF-8'
    结论:此路不通
  2. 特定字符串调试
    使用PyCharm调试程序,选定特定room_name值,来进行分析
    # 注意此时输出标记为u,说明识别为unicode编码,正常时此时print出是真实值
    >>> room_name
    u'\xbf\xec\xb5\xdd\xbc\xe4\xa3\xa8\xc3\xc5\xc4\xda\xa3\xa9'
    # 打印原始值为乱码,所以怀疑实际存储的是被标记为unicode的其他编码
    >>> print room_name
    ¿ìµÝ¼ä£¨ÃÅÄÚ£©
    # 这时可以将引号内赋值,再使用chardet.detect()判断
    >>> aa='\xbf\xec\xb5\xdd\xbc\xe4\xa3\xa8\xc3\xc5\xc4\xda\xa3\xa9'
    >>> aa
    '\xbf\xec\xb5\xdd\xbc\xe4\xa3\xa8\xc3\xc5\xc4\xda\xa3\xa9'
    # 果然,检测出的结果是GB2312编码
    >>> chardet.detect(aa)
    {'confidence': 0.99, 'encoding': 'GB2312'}
    # 输出正常
    >>> print aa.decode('gb2312')
    快递间(门内)
    # 此时,需要unicode->encode('Latin1')->decode('GB2312')
    >>> room_name.encode('latin1').decode('GB2312')
    u'\u5feb\u9012\u95f4\uff08\u95e8\u5185\uff09'
    >>> print room_name.encode('latin1').decode('GB2312')
    快递间(门内)

解决办法

  pymssql基础实现使用的是cpython,从GitHub的官方代码文件_mssql.pyx,可以看到一些处理过程。使用strcpy函数对数据交换,因为对cpython不了解,怀疑是在处理双字节文字转码时的一点bug。

  这个问题有两个解决办法:

  1. 代码中显式转码
    方法:unicode变量.encode('latin1').decode('gbk'),详细情况可以参考下方的“PYTHON-进阶-编码处理小结
    一般情况下对unicode编码不做encode处理,但必要时可以encode为Latin1,实现脱unicode操作,然后再以合适字符集decode为正确unicode

    print "\t".join([str(room_id),room_name.encode('latin1').decode('gbk')])
  2. 字符定义使用NVARCHAR
    这种方式在存储和读取时都使用unicode编码,和python运转字节码一致,可以很好避免此类问题。当然数据库存储空间要牺牲一些。
    [room_name] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NULL 

原文:这里

参考:

1)"UnicodeDecodeError: ‘gbk’ codec can’t decode bytes in position 2-3: illegal multibyte sequence"

2)水木社区:用pymssql的时候出现了很诡异的字符集问题

3)PYTHON-进阶-编码处理小结

pymssql读取varchar字段中文显示乱码的问题分析的更多相关文章

  1. c# 关于抓取网页源码后中文显示乱码的原因分析和解决方法

    原因分析:首先,目前大多数网站为了提升网页浏览传输速率都会对网站内容在传输前进行压缩,最常用的是GZIP压缩解压解压算法,也是支持最广的一种. 因为网站传输时采用的是GZIP压缩传输,如果我们接受we ...

  2. C#.NET ORM FreeSql 读取使用 US7ASCII 的 Oracle 数据库中文显示乱码问题

    前言 关于 Oracle US7ASCII 中文乱码的问题,Ado.Net 和 Odbc 无法解决.包括最新的.Net Core..NET6..NET7 都无法解决这个问题. FreeSql 对 Or ...

  3. MySQL 中文显示乱码以及中文查询条件返回0条结果的问题解决

      最近关于中文显示乱码的贴子比较多,所以也做了个总结: 可以参考一下杨涛涛版主的<各种乱码问题汇总>http://topic.csdn.net/u/20071124/08/3b7eae6 ...

  4. MySQL 中文显示乱码

    最近关于中文显示乱码的贴子比较多,所以也做了个总结: 可以参考一下杨涛涛版主的<各种乱码问题汇总> http://topic.csdn.net/u/20071124/08/3b7eae69 ...

  5. SecureCRT中文显示乱码

    环境:SecureCRT登陆REDHAT5.3 LINUX系统 问题:vi编辑器编辑文件时文件中的内容中文显示乱码,但是直接使用linux系统terminal打开此文件时中文显示正常,确诊问题出现在客 ...

  6. Linux中文显示乱码?如何设置centos显示中文

    Linux中文显示乱码?如何设置centos显示中文 怎么设置Linux系统中文语言,这是很多小伙伴在开始使用Linux的时候,都会遇到一个问题,就是终端输入命令回显的时候中文显示乱码.出现这个情况一 ...

  7. Xshell个性化设置,解决Xshell遇到中文显示乱码的问题

    在同事的推荐下,今天开始使用Xshell连接Linux,但是发现一个“遇到中文显示乱码”的问题, 同事的解决方案如下: 平常给Linux上传文件之前,先把文件转换成UTF-8编码形式, 然后设置Xsh ...

  8. (转)sqlplus中文显示乱码的问题

    sqlplus中文显示乱码的问题 2010-07-19 11:33:26 分类: LINUX 在windows下sqlplus完全正常,可是到linux下,sqlplus中文显示就出问题了,总是显示“ ...

  9. GB2312、GBK和UTF-8三种编码以及QT中文显示乱码问题

    1.GB2312.GBK和UTF-8三种编码的简要说明 GB2312.GBK和UTF-8都是一种字符编码,除此之外,还有好多字符编码.只是对于我们中国人的应用来说,用这三种编码 比较多.简单的说一下, ...

随机推荐

  1. 成长笔记 - mysql-5.5.25-winx64安装步骤(及密码修改问题)

    操作系统:Windows 7 64位 下载地址: http://download.mysql.cn/src/2012/0602/5611.html 1. 将mysql-5.5.25-winx64.zi ...

  2. Java Enum枚举的用法(转)

    说明:Java的枚举比dotnet的枚举好用,至少支持的方式有很多. 用法一:常量 在JDK1.5 之前,我们定义常量都是: public static fianl.... .现在好了,有了枚举,可以 ...

  3. JDK动态代理理解精髓

      1.Java动态代理的关键是:Proxy类要和InvocationHandler的接口实现类,要用同一个目标target对象class,所以精髓是InvocationHandler和Proxy是一 ...

  4. PR物料KFF弹出LOV - WHERE条件重写

    PROCEDURE event (event_name VARCHAR2)IS---- This procedure allows you to execute your code at specif ...

  5. 常见的哈希Hash算法 & MD5 & 对称非对称加密 & 海明码

    参考 Link 另外,这篇文章也提到了利用Hash碰撞而产生DOS攻击的案例: http://www.cnblogs.com/charlesblc/p/5990475.html DJB的算法实现核心是 ...

  6. keras模型总结

    https://keras.io/zh/ https://keras.io/zh/models/about-keras-models/ 在 Keras 中有两类主要的模型:Sequential 顺序模 ...

  7. c++中c_str()函数

    https://zhidao.baidu.com/question/104592558.html

  8. AAuto如何发布EXE文件

    1 如下图所示,谷歌翻译是AAuto提供的源码,我们现在把它做成软件.点击编译,注意看底部状态栏提示,编译之后的谷歌翻译还是aau格式的,双击可以直接运行.但是体积变大了,而且已经是二进制文件,无法再 ...

  9. linux系列之-—03 压缩和解压缩命令

    tar命令 解包:tar zxvf FileName.tar 打包:tar czvf FileName.tar DirName gz命令 解压1:gunzip FileName.gz 解压2:gzip ...

  10. ActionFilterAttribute之HtmlFilter,压缩HTML代码

    当开启这个过滤器后,最终生成的HTML代码将会被压缩一下,在流量很大的网站中,能减少带宽成本就减少一点,何乐而不为? [csharp] view plaincopy using System; usi ...