明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 2038|回复: 2

[转帖]给lisp添加 GBK 编码支持

[复制链接]
发表于 2010-7-29 12:18:00 | 显示全部楼层 |阅读模式

为 SBCL 添加 GBK 编码支持

Lisp 2006-12-22 00:18:38 阅读451 评论0 字号:大中小

有时很羡慕 Perl 程序员:CPAN 太伟大了,各种模块应有尽有。而 Lisp 的配套资源相对要比 Perl, Python, Java 少很多。不过我是 Lisp 程序员,在找不到现成设施的情况下,只好自己动手丰衣足食了。

最近遇到的严重问题是 SBCL,我用的主要 Lisp 平台,不支持 GBK 编码。Lisp 显然比其他语言更懂得字符的含义,对于一个字符流来说,如果没有指定明确的编码,那么默认值可能是 ASCII 或者 UTF-8,但对于含有 GBK 编码字符的流来说,读入操作将会因为无法解码而报错。如果我以字节流的方式读取,那么没有任何问题,但是牺牲了效率:本来我可以一次读一行的,现在只能一个一个字符地读了。

CLISP是所有Lisp里支持编码种类最多的,但我看了CLISP的代码以后发现,这主要是因为CLISP引用了第三方字符集转换C库和GNU C库来得到支持。SBCL的特点是C代码尽可能小,主要语言逻辑用纯Lisp实现,因此每一种编码支持都要用Lisp实现,所以开发者不实现GBK也是情有可原的。SBCL中唯一支持的东方语言编码是主要的几种日文编码,为了实现日文编码和UCS的转换,必须提供完整的编码转换表。我又读了 GNU C 库里关于 GBK 编码的源文件,奇迹般地发现我可以看懂:几张巨大的编码转换表直接以C语言二维数组的形式写在源代码里……于是我从网上随便搜了一张GBK到UCS的编码转换表,就开始自己的移植工作了。

首先,SBCL的编码支持位于 sb-impl 包里。Lisp 的一个明显的好处是修改语言实现时无须重新编译整个代码,在现有的环境下替换或者添加一些函数和变量即可。

下面的程序首先解决了编码转换的接口部分:(enc-cn.lisp)

(in-package :sb-impl)

;;; GBK
(declaim (inline ucs-to-gbk gbk-to-ucs
                 mb-len-as-gbk gbk-continuation-byte-p))

(defun ucs-to-gbk (code)
  (declare (optimize speed (safety 0))
           (type fixnum code))
  (if (<= code #x7f) code
      (get-multibyte-mapper *ucs-to-gbk-table* code)))

(defun gbk-to-ucs (code)
  (declare (optimize speed (safety 0))
           (type fixnum code))
  (if (<= code #x7f) code
      (get-multibyte-mapper *gbk-to-ucs-table* code)))

(defun mb-len-as-gbk (code)
  (declare (optimize speed (safety 0))
           (type (unsigned-byte 8) code))
  (if (< code #x80) 1 2))

(defun gbk-continuation-byte-p (code)
  (declare (optimize speed (safety 0))
           (type (unsigned-byte 8) code))
  (<= #x80 code))

(define-multibyte-encoding :gbk (:gbk :|GBK| :cp936 :|936|)
  ucs-to-gbk gbk-to-ucs mb-len-as-gbk gbk-continuation-byte-p)

ucs-to-gbk 和 gbk-to-ucs 是编码转换函数,他们非常简单,当需要转换的字符超出 ASCII 范围(0x7f)时,直接到一张巨大的码表里查询即可。mb-len-as-gbk 用于在处理多字节字符集时判定一个字符共有多少字节。对于 GBK 来说,最简单的处理就是当第一个字符在 ASCII 范围之内时程度为1,否则长度为2。gbk-continuation-byte-p用于在处理编码时确认一个字节是位于字符的起始还是中间,我的简单处理是认为当字节不在ASCII范围内时就一定不是一个GBK字符的开始。最后 define-multibyte-encoding 用上述函数构造一个完整的多字节编码环境,它是一个宏,实际上会生成许多东西。

另一个文件就显得无趣多了:(enc-cn-tbl.lisp)

(in-package :sb-impl)

(define-multibyte-mapper *ucs-to-gbk-table*
    '((#x4e02 #x8140) (#x4e04 #x8141) (#x4e05 #x8142) (#x4e06 #x8143)
      (#x4e0f #x8144) (#x4e12 #x8145) (#x4e17 #x8146) (#x4e1f #x8147)
      (#x4e20 #x8148) (#x4e21 #x8149) (#x4e23 #x814a) (#x4e26 #x814b)
      (#x4e29 #x814c) (#x4e2e #x814d) (#x4e2f #x814e) (#x4e31 #x814f)
      ...
      (#xfa13 #xfe45) (#xfa14 #xfe46) (#xfa18 #xfe47) (#xfa1f #xfe48)
      (#xfa20 #xfe49) (#xfa21 #xfe4a) (#xfa23 #xfe4b) (#xfa24 #xfe4c)
      (#xfa27 #xfe4d) (#xfa28 #xfe4e) (#xfa29 #xfe4f)))

(define-multibyte-mapper *gbk-to-ucs-table*
    '((#x8140 #x4e02) (#x8141 #x4e04) (#x8142 #x4e05) (#x8143 #x4e06)
      (#x8144 #x4e0f) (#x8145 #x4e12) (#x8146 #x4e17) (#x8147 #x4e1f)
      (#x8148 #x4e20) (#x8149 #x4e21) (#x814a #x4e23) (#x814b #x4e26)
      ...
      (#xfe49 #xfa20) (#xfe4a #xfa21) (#xfe4b #xfa23) (#xfe4c #xfa24)
      (#xfe4d #xfa27) (#xfe4e #xfa28) (#xfe4f #xfa29)))


长达 10000 行的枯燥文本,用于明确定义 GBK 和 UCS 直接的编码关系。我的表格是从
http://www.opensource.apple.com/darwinsource/Current/libiconv-13/libiconv/tests/GBK.TXT 下载的,很容易用 Emacs 把格式转换成 Lisp 可读的形式。

当上述两个文件编辑并加载进 SBCL 以后,SBCL 就具有了理解 GBK 编码的能力了,下面这个小测试可以帮助确定代码的有效性:

准备一个含有 GBK 字符的文本文件 sample.txt,在 UTF-8 环境下试图读出并打印它的第一行:

(with-open-file (s "sample.txt" :direction input :external-format :gbk)
  (read-line s nil nil nil))
 楼主| 发表于 2010-7-29 12:19:00 | 显示全部楼层
上面是转载的一篇博文,作者在lisp平台增加了GBK 编码支持,不知道cad的lisp平台能够同意实现呢
 楼主| 发表于 2010-8-4 00:37:00 | 显示全部楼层

竟然沉了,大大们帮忙想想拉,拜托

您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2024-10-2 08:27 , Processed in 0.149501 second(s), 25 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表