函数 start-process 传递参数的编码问题

这个问题我研究了两天无法解决,希望大家可以教我。

起因是使用 emms 可以正常显示,但是无法播放韩语、泰语文件名的歌曲。后来找到原因是 start-process 函数无法正确传递 utf-8 编码的参数,与 emms 无关。

函数描述: (start-process NAME BUFFER PROGRAM &rest PROGRAM-ARGS)

我在纯净的 emacs 下这样测试的: (start-process “test1” nil “notepad” “시린”)

notepad 得到的参数只能是 “鞁滊Π” 这样的乱码,我尝试过修改了很多关于编码的选项都无济于事。

比如

(set-language-environment “utf-8”)

(setq default-process-coding-system '(utf-8 . utf-8)) ;; 如果修改为 cp936 那么除英中日文正常,其他都会变成空白,修改为 utf-8 则除英文外全部乱码。

(w32-set-console-codepage '65001) ;;怎么修改都无变化

(w32-set-console-output-codepage '65001) ;;怎么修改都无变化

平台:win8.1 64/ win7 64

编译版本:官方编译版本 25 / 26 / 27 纯净无添加

cp936 不支持韩国字。

可能要改系统的编码,不是很了解这块(chcp 只影响命令行?)。


有没有人试过 Windows 10?

终于发现有同僚也发现这个问题了

起初我因为觉得这个问题太小众了,懒得提问了,既然有同僚了那就水一下:

看楼主也是在win下得到这个问题的吧,发一下我的平台信息:

  • Operation-system: win10 1709
  • emacs-version: GNU Emacs 26.1 (build 1, x86_64-w64-mingw32) of 2018-04-10

我的问题:

一开始我在windnows中使用emacs想要调用外部程序, 我使用了函数 w32-shell-execute, 这个函数有一个特点:

它是使用内部钩子调用win的api来联动win下与该文件类型关联的程序: 比如这个钩子: open

如果要实现外部打开一个文件,可以这样:

(w32-shell-execute "open" "file-name")

在函数参数描述中,形式是这样的:

(w32-shell-execute OPERATION DOCUMENT &optional PARAMETERS SHOW-FLAG)

这让我很开心但是有一个不满足的地方,就是,无法调用指定程序打开某一个文件。

于是我再仔细看了一下这个函数的描述文档,发现它是可以使用参数的,也就是这个optional参数选项中的第一个位,并且我发现"DOCUMENT"这个参数是既可以是文件也可以是可执行程序的,那么联合额外参数,就可以使用指定程序打开一个文件了:

(w32-shell-execute "open" "D:/PortableApps/mpc/bin/mpc-hc.exe" "\"E:/episode/Downton Abbey/1.mp4\"")

可是我发现这个参数只能使用本地locale和latin两种编码,不能使用其他的诸如韩语,法语等,如果把一个韩语命名的文本文件传入nodepad++的话,文件名是乱码的,然后外部程序提示,无法找到文件!

于是乎我选择使用 universal-coding-system-argument 来临时调用一下韩文编码,依然乱码,。。。。。。。。。卒 :sob:

求救中。。。。。

1 个赞

假定你升级了Windows10最新更新

  1. 打开系统Region & Language 设置
  2. 往下滚动,选择Related settings中的Administrative language settings
  3. 选择Change system local…
  4. 勾选 Beta: Use Unicode UTF-8 for worldwide language support

好了,进入Emacs,执行(setq w32-system-coding-system 'utf-8) (w32-shell-execute “open” “notepad.exe” “中文文件名.txt”)应该正常了

2 个赞

我们遇到的有可能是一个问题。

我有个临时办法来解决,就是自己写个程序,处理 emacs 传递过来的参数。比如:

(start-process "test1" nil "program.exe" "utf-8 编码的参数")

program.exe 接到的参数是乱码,可以经过编码转换变得正常,然后 program.exe 再利用正常的参数调用其他程序。

program.exe 的 c# 代码简单例子:

string newArg = Encoding.UTF8.GetString(Encoding.Default.GetBytes(arg));
Process p = new Process();
p.StartInfo.FileName = "notepad++.exe";
p.StartInfo.Arguments = newArg;
p.Start();

好主意 我不会c#或cpp,准备试一下C。

这个方法我觉得是不会有用的,因为参数在传递给外部程序以前已经丢失了。 如果你观察Emacs源码w32fns.c的Fw32-shell-execute函数,你会看到参数是用locale-coding-system编码的(也就是w32-system-coding-system):

parameters = ENCODE_SYSTEM (parameters);

然后再用当前系统代码页转换到utf-16:

len = pMultiByteToWideChar (CP_ACP, multiByteToWideCharFlags,
			      SSDATA (parameters), -1, NULL, 0);

为了保留多国语言字符,必须确保每一步都保留了unicode。

(setq w32-system-coding-system 'utf-8) // 或者 (setq locale-coding-system 'utf-8)

保证了第一步能够在parameters里得到utf-8的字符串

把系统代码页设置成utf-8可以保证第二步从utf-8转换到utf-16

如果不改系统代码页,直接改Emacs源代码,用CP_UTF8估计也可以。

1 个赞

有意思的是w32-shell-execute的第二个参数document并不需要这么折腾,随便放任意语言的字符串都可以。因为源码里这个参数是用file-name-coding-system编码的,在windows上默认就是utf-8:

document = ENCODE_FILE (document);

这看来是要升级insider开发版的节奏哇,我是怕不稳定,再来win7是没有这个选项的。

公司电脑是win7, 看来只有按照你下面一条提示回复所说的改源代码了,但是这个patch会不会牵连其他内置组件?风险会不会很大?

二来,我现在的想法是,用elisp写一个小函数把文件名传入到一个temp文件,然后调用外部的一个C的几行代码的操作函数提取它,然后运行。 下午试一下。

win10不需要上insider channel,最新的公开版本已经有这个选项了(虽然上面标着beta)

谢谢提醒, 好久回顾此贴。现在在测试magit,之前由于magit在codepage为65001下无法工作,因此一直耽搁了。

正好搜start-process的时候看到这个贴子,start-process底层就是调用make-process

(defun start-process (name buffer program &rest program-args)
  (unless (fboundp 'make-process)
    (error "Emacs was compiled without subprocess support"))
  (apply #'make-process
	 (append (list :name name :buffer buffer)
		 (if program
		     (list :command (cons program program-args))))))

make-process是有 coding参数的