关于format函数的一个奇怪问题

我的配置因为未知的原因似乎会影响format函数的行为,具体如下

(format (concat "%50s") "Boutet de Monvel’s calculus and groupoids I")
(format (concat "%50s") "Boutet de Monvel's calculus and groupoids I")

注意在以上两行中唯一的区别在于单引号被替换成了一个非ascii的注音符号。原则上说这两条语句输出的应该是长度相同的两个字符串。如果在没有任何配置的emacs中测试,结果也的确如此。但是在我的配置中上边一行输出的字符串要比下边一行输出的字符串短一个字符。似乎format函数错误的忽略了。按说format是一个C编码的函数,不知道为何会出现这个问题。

我的配置基本上用的是purcell的配置,有点难以定位他在哪里有相关的改动。不知各位道友有何见解?

或许可以先用二分法排除一下?

我用二分法测试了我自己添加的一些配置,就算把我自己的配置完全去掉,结果问题仍然有,剩下他的配置文件个数有些多,有点难以下手。

  1. (concat "%50s") 改成 "%50s" 有没有关系?

  2. 基本上只有 locale 能影响 format 的行为,试试把相关的去掉。

  3. 系統,Emacs 版本,语言环境?

我的配置也是基于 Purcell 的配置改的,基于你的代码。运行结果的长度是一样的。

确实,我用purcell配置的最新版试了试的确也是正常的。那这到底是什么问题呢?奇怪

如果把init.el文件的内容全部注释掉,这个问题仍然存在。但是如果用emacs --no-init-file启动emacs的话,这个问题就消失了。

这二者有什么区别吗?一个空的init文件和没有init文件不是一样的吗?

不妨把你的配置贴出来康康

你怎么测试的?

  1. 使用 init.el 的时候 Emacs 版本多少?
  2. 使用 emacs --no-init-file 的时候 Emacs 版本多少?
  3. 系统是什么?

多半是字体问题 在等宽字体下显示出来的宽度跟'相同,非等宽字体下显示出来的宽度是'两倍。所谓没有配置跟有配置实际上都是上边的比下边的短一个字符,你之所以看到所谓差异实际上是被等宽字体给遮蔽了。

我查看了一下 format 的文档,里面是这样介绍的

The width specifier supplies a lower limit for the length of the
printed representation.  

个人猜测:打印的时候,默认就是'两倍的宽度,我尝试使用 emacs -Q 与 emacs 正常启动作了对比,虽然看起来是下面这样的:

貌似, emacs -Q的似乎更加合理一点,但是只要用 length 来测试一下,就原形毕露了,不论哪个测试的最终结果都是一个 49 一个 50,只是显示差异罢了。

字体只影响显示效果,对实际宽度无影响。

如果按字节算,相差就不止 1 了:

(string-bytes "“") ;; => 3
(string-bytes "’") ;; => 3
(string-bytes "'") ;; => 1

(length "“")       ;; => 1
(length "’")       ;; => 1
(length "'")       ;; => 1

我还是个人看法,不一定对:%s 这玩意本质上就是打印人眼可读的格式,换句话说,就是打印一个让你看起来貌似是没有问题的格式。Emacs 的处理方式就是 占位两倍于 ',所以在 format 计算距离(width)的时候默认按 2 进行计算,但实际上按字符算他还是 1。

或者更确切的说 %2s 中间的 2 既不是 字节长度也不是字符长度,而是被称为宽度特殊存在,这个宽度是用来的。

至于 length 或者 string-bytes 都可以用来查看生成新字符串的字符长度或者字节长度是否一致,用哪个都一样,我上面用的是 length。

你如果知道重现方法,就帮楼主补一个。代码或具体步骤都行。

就字体问题啊,我截图里不是已经重现了吗?有的字体(比如Emacs 默认字体)占位距离为 '两倍,我配置里用的字体(Sarasa)占位距离与 '相同。

但无论显示的距离如何,Emacs 在实际计算宽度的时候都是上边补 6,下边补 7,因为Emacs 默认占 2, ' 占 1

我的emacs版本是27.2,系统是manjaro。

我不太确定是不是字体的原因。我之前说用emacs --no-init-file启动没有这个问题,而直接启动emacs有问题。但是我发现这种说法不对。因为我所谓的直接启动emacs是借助于rofi来启动的,而不是直接在终端中启动输入emacs来启动。

实际上我发现只要在终端中启动emacs,无论是否加载配置文件,都没有这个问题。而借助于rofi启动emacs,同样无论是否加载配置文件,这个问题都会出现。所以我怀疑就像@LdBeth所说,应该是不同启动方式导致emacs继承了不同的环境变量,所以会出现这个奇怪问题。

又试了试,用rofi启动current-language-environment的值是Chinese-GBK,而使用终端启动的话这个变量的值是English。如果在配置文件中直接将这个变量的值设成English的话,两种启动方式都不会再出现format函数的问题了。

(length (format "%3s" "'")) ;; => 3
(length (format "%3s" "~")) ;; => 3
(length (format "%3s" "@")) ;; => 3
(length (format "%3s" "ቷ")) ;; => 3
(length (format "%3s" "步")) ;; => 2
(length (format "%3s" "〈")) ;; => 2
(length (format "%3s" "💏")) ;; => 2
(length (format "%3s" "ㅿ")) ;; => 2
(length (format "%3s" "͈")) ;; => 4
(length (format "%3s" "𞥆")) ;; => 4
(length (format "%3s" "​")) ;; => 4 ;; ZERO WIDTH SPACE

估计是按 unicode 的显示宽度来的

关于emacs的locale设置我又有个不理解的现象。如果在init文件中手动设置current-language-environment变量的值的话,无论是将其设置成English还是Chinese-GBK,在使用C-x C-f打开新文件时,候选文件名列表中的中文字符都是乱码,只有将这个变量设置成UTF-8,才会正确的显示中文文件名。

但是用终端或者rofi打开emacs时,虽然这时查看current-language-environment的值是English或者Chinese-GBK,中文文件名却也可以正常显示。这是为什么?难道emacs在启动过程中还设置了其他与locale有关的变量?

因为用终端打开的时候,直接传递给终端的是原始数据,终端本身不受 locale 影响。

我主要是不明白emacs在启动过程中将current-language-environment设置成Chinese-GBK,和emacs启动之后我手动将current-language-environment设置成Chinese-GBK有什么区别?为什么一个有乱码一个没有?