« 又折腾了 Link Station Pro 一天 | 返回首页 | IDE 不是程序员的唯一选择(五) »

解决 RTorrent 部分中文文件名乱码

这两天在我的 LS Pro 上装 BT 软件。以前我在 freebsd 下都是装的 ctorrent ,这次想换个别的。看中了 RTorrent ,项目维护还比较勤,新版支持 DHT 。不过 debian 的源上比较老,官网上那个源我连不上。所以就下源代码自己编译了一个。

原来以为这种小东西没多少代码量的,本地编译就够了。可惜我轻视了 C++ 代码编译的龟速。在 LS Pro 上,居然一个短短的 .cc 文件就要编译 20 秒左右。有点后悔没有用交叉编译,不过忍了几小时也就过去了。

新版本支持 xmlrpc 的控制协议。我就可以装一个 web 界面管理了 :) 我选的是 rtgui 还不错。

本来以为一切都搞定可以庆祝了的。没想到试了一个种子,其中中文文件名出现乱码,连带的导致了 rtgui 工作不正常 :( 然后花了一整夜的时间来折腾这个,好不辛苦。

按搜索来的信息,新版 rtorrent 在配置文件里加一行 encoding_list = utf-8 就可以支持中文。我试了以后,大多数种子都是正常的。但是少部分种子下载后不正确。

对比了两个种子文件,发现在种子文件头上有诸多文件名。而正确的文件名前都标有 utf-8 ,而出问题的文件名前是没有注明编码方式,而直接用的 GBK 编码。

我猜想 rtorrent 对于没有标明编码的字符串,一律不做任何转换,直接创建文件。通过阅读源码证实了这一点。唉,开源就是好啊,出了问题可以自己检查。当然了,在 rtorrent 华丽的圆环套圆环的 C++ 封装下,看透这个本质还是需要点时间和功力的,尤其是在我只有 vi 和 grep 等不多的命令行工具的情况下。

google 了老半天,愣是没一个亚洲人关心这个问题。通过阅读的源码,我确信这个问题是不能通过修改软件配置来解决的,因为 rtorrent 的源代码里,就没有任何编码转换的函数。

最终,我决定自己动手,丰衣足食。人家都开源了,就是鼓励你自己修改不满意的位置吧。因为我只想快点解决问题,而不是帮软件增强功能。所以我决定把补丁写死在代码里。遇到没有声明编码格式的文件名,通通从 GBK 转换为 UTF-8 。

rtorrent 是 C++ 写的,看起来很漂亮,一个类套一个类,层次分明,抽象的很好。当然也有不足的地方,就是光抽象,不好好干活,花了大量的代码做出一个个漂亮的对象,最终就干一点事情。不小心还弄点飞线出来用来直达目标,不过因为抽象层代码很多,所以飞线占的比例就自然很少啦。90% 的代码都是整洁的,10% 的代码又那么丁点坏味道。哦,对了,那 10% 的代码是真正干活的部分,做苦力的地方嘛,不用太注意干净了。

通过阅读 rtorrent 的代码,我又一次充分认识到:C++ 是怎么把每一件不起眼的小工作发挥的如此叹为观止,充分体现出一个高素质的 C++ 程序员的价值所在的 :)

下面说正题:经过一番分析,我确定了在 libtorrent 中,有个叫 Path 的类是用来处理文件名的。简单浏览了 path.h 文件,发现 Path 私有继承了一个 string 的 vector ,想必是保存了文件路径中的一段段字符串。既然是私有继承,我就比较放心了,只看 public 出来的接口就行。有个 as_string 的接口吸引了我,这个是看起来唯一可以取出 Path 内部字符串的接口。另外 m_encoding 变量也是 private 的,保存了编码方式。按我的预料,当 encoding 字符串为空的时候,就可以打我那个补丁了。

本地编译 C++ 文件实在是慢,所以我尽量不想修改 .h 文件改动接口。就从修改 path.cc 开始。在 as_string 里判断一下 encoding 是否为空,然后调用 iconv 转换编码。

只用了不到 10 分钟改完,重新编译一个文件,然后重新做试验。发现,问题依旧 :( 。不过似乎又和以前不太一样。我写了几行 log 重新测试,确认我的代码正常工作了。

这让我不得不怀疑,rtorrent 在 Path 类里跳了飞线。仔细研究了一下 .h 又 grep 了全部代码。我发现,Path 居然给出了 begin end 得方法,返回 private 继承的那个 vector.string 的 iterator 。而且,还有一个 public 方法叫 base ,可以返回 private 的基类。天哪,那个 private 继承不都暴露了,写上 private 只为了一个华丽的存在,来忽悠我这个曾经的 C++ 爱好者么?

进一步浏览代码,确信在创建目录时,它用了这根飞线,直接访问了 private 基类的数据。所以我的修改没完全生效。

经过一分钟的判断,我想我不改 .h 文件很难了。因为 C++ 要给 class 加一成员方法是不能像 C 那样另写在一处的。天啊,修改 path.h 会影响到几十个 .cc 文件重编译(在我的 LS Pro 上,这意味超过半小时的时间),虽然它们并不会用到我新加的方法。

最终我给 Path 加了一个在未指定编码时,把内部字符串转换为 utf-8 的成员方法。(理论上不增加这个方法也可以,可以在 Path 内部实现里加一个华丽的模块,可以自动转换,不过我放弃了这种做法)

最后修改一个叫 download_constructor.cc 的文件,源文件末尾实现的那个函数 DownloadConstructor::choose_path 里调用了一下新添加的转换函数。一切正常了。所有中文文件名显现出了它们应有的 utf-8 编码的和谐姿态。一切都是那么完美,除了源代码中那个难看的,一点都不 C++ 风格,一点都不华丽的补丁。


2009 年 1 月 30 日补充:

虽然上面已经说的很清楚了,但是依旧有朋友不想自己动手。我把 patch 放在这里好了。 不过这个解决方案绝对一点都不优雅,是我当时随手做来应付的。patch 上后,需要安装 iconv 才可以编译。

点击下载(libtorrent-0.12.3.patch)

不要再问我如何 patch 如何编译的细节。

Comments

我用的是ctorrent下载时文件名也出现乱码的现象啊,能不能解决啊,

前几天刚编了新版的rtorrent 0.8.6/libtorrent,0.12.6,请问有对应0.12.6的patch么?

不错

saaaaaaaaa

我一直都把private基类,当成员看待。但是他把名字叫base,委实不妥。这个path封装也劣了一点了。

庖丁解小強,大牛國慶大假的休閑娛樂方式........

@Sparkle
理解错误??想想你说的是云风的RTorrent……囧rz

@Sparkle
优雅的patch,就是用Objective Calm那个奇怪的语言了……lifc说过了,它每个文件里let了一大堆东西,然后就在那里搞match,就此就OO了,看着实在是别扭。

@peter
如果你想装Windows XP,你需要一颗x86/x64的CPU,请先补补计算机基础知识

对了,我也需要一个家庭上网网关,但是必须要可以装XP。用这个合适不会说呢

我还是不明白为啥要买一个这东西玩呢?

能不能装XP啊?

做一个优雅的patch提交给官方吧,我想很多人需要

他的BT协议确实有点问题,从05年就提出到现在官方都没处理。之前搞了个c写的DHT patch费了不少力气合到mlnet上,但有时会导致segment fault,不太熟悉Ocalm语法一直没找到原因,暂时只好用个脚本看着mlnet,崩溃了就重启:(

mldonkey 我也装了。下电驴可以,BT 不支持 DHT ,而且好多 tracker 协议不认,无视了。

可以试试MLDonkey,支持的协议很全,我们的嵌入式产品(ARM9)里面就跑这个,GPL的License,不改的情况下拿去卖产品也没问题,且中文支持也不错。就是他的开发语言太高级了(Ocaml),看了半天都不明白。

我也觉得对C++很困惑,我一度曾经非常C++化,但现在回想起来,也太累了.就一个空类,为了达到C++清教徒的标准,你得写多少东西?
首先你得确认你的类是不是要被继承,如果要被继承,那么析构函数要自己写.然后拷贝构造函数,然后等号重载....加一个方法,就得考虑是不是const,每一个参数也得考虑要不要const&.
现在想起来就要疯掉,可当年不知为什么对此乐此不疲..

我是格言是

“自己动手,丰衣足食。他人动手,衣食无忧”

我觉的可以用注入方式不需要一定要去改,好比在我们运行前做一个拦截器一样的东东,这个东东不影响C++原有结构,这种规则在JAVA SPRING里头的可以照套到C++,不过还是要配置XML告诉编译器。

这种“伪封装”在C++程序中很常见,做出封装的架势,但却不封装。

看不懂,不过看出来确实是很牛逼很有成就感的抱怨贴。哈~

Post a comment

非这个主题相关的留言请到:留言本