pymssql读取varchar字段中文显示乱码的问题分析

摘要:
根据默认情况下,SQLServer2000使用ISO字符集。该字符集也称为ISO-8859-1Latin1或ANSI字符集。它与Windows9x和WindowsNT/2000操作系统兼容,并提供与大多数语言的最大兼容性。SQLServer2000还包括代码页936,其中包含支持简体中文的字符。设置字符集更合适查看pymssql用法文档,发现官方没有提供此参数可以接受的实例字符串。strcpy函数用于数据交换。因为我不了解cpython,我怀疑它是处理双字节文本转码时的一个错误。当然,需要牺牲数据库存储空间。

问题

用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 "	".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: Unexpected EOF from the server DB-Lib error message 20002, severity 9: Adaptive Server connection failed ')
    <charset='cp936'>调试模式下pymsql.connect无异常信息,但程序直接退出
    <charset='utf8'>运行正常,输出依然乱码;不指定此参数值时,程序使用默认值'UTF-8'
    结论:此路不通
  2. 特定字符串调试
    使用PyCharm调试程序,选定特定room_name值,来进行分析
    # 注意此时输出标记为u,说明识别为unicode编码,正常时此时print出是真实值
    >>> room_name
    u'xbfxecxb5xddxbcxe4xa3xa8xc3xc5xc4xdaxa3xa9'
    # 打印原始值为乱码,所以怀疑实际存储的是被标记为unicode的其他编码
    >>> print room_name
    ¿ìµÝ¼ä£¨ÃÅÄÚ£©
    # 这时可以将引号内赋值,再使用chardet.detect()判断
    >>> aa='xbfxecxb5xddxbcxe4xa3xa8xc3xc5xc4xdaxa3xa9'
    >>> aa
    'xbfxecxb5xddxbcxe4xa3xa8xc3xc5xc4xdaxa3xa9'
    # 果然,检测出的结果是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'u5febu9012u95f4uff08u95e8u5185uff09'
    >>> 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 "	".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字段中文显示乱码的问题分析》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Visio文件转EPS文件vue的坑下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

关于 freetds pymssql 的安装部署

关于 freetds pymssql 的安装部署一、安装: (freetds-0.91 pymssql 2.0.1) 如果要在linux机器 连mysqlsever 1.需要安装freetds./configuremake && make install 测试tsql -C查看安装的版本tsql -H 10.20.17.102 -p 143...

python django 连接 sql-server

1.准备工作 python3.6连接sqlserver数据库需要引入pymssql模块 pymssql官方:https://pypi.org/project/pymssql/ 没有安装的话需要: pip安装: pip install pymssql   2.连接数据库 首先你得明确目标数据库的:'服务器名称',"账户名称","密码","数据库名称"...

Windows下安装pymssql

准备用Python接入Sql Server数据库,因此准备用pymssql模块。 安装有点纠结。 64位win10系统,python3.6   步骤: 首先需要配置一下freetds: 在这里下载:https://github.com/ramiro/freetds/releases 下载:freetds-v0.95.95-win-x86_64-vs2015...

python 3.6 链接mssql 进行数据操作

#!/usr/bin/env python # -*- coding: UTF-8 -*- import pymssql class MSSQL(object): ''' 对pymssql的简单封装 pymssql库,该库到这里下载:http://www.lfd.uci.edu/~gohlke/pythonl...