Windows 版 R 4.2.1 中即将进行的更改



R 4.2.1 计划于下周发布,其中包含许多针对 Windows 的特定修复。目前使用 R 4.2.0 的所有 Windows R 用户都应升级到 R 4.2.1。本文将详细介绍一些修复内容。

Windows 版 R 4.2.0 带来了重大改进。它使用 UTF-8 作为本机编码,为此它切换到了通用 C 运行时 (UCRT)。这反过来又需要为 Windows 创建一个新的 R 工具链,并使用它重新构建 R、R 包和所有(静态链接的)依赖项 (Rtools42有关过渡的更多详细信息)。

使用 UTF-8 作为本机编码可以显著减少在处理 Windows 通常使用的编码中无法表示的字符时出现的编码转换问题,例如在运行于欧洲、美洲或任何其他使用拉丁字母的地区中的系统上出现的亚洲字符问题。

R 4.2.0 在发布之前已使用 CRAN 和 Bioconductor 包进行了定期测试,但用户在发布后发现了自动化 R/包测试未涵盖且有限的手动测试未发现的几个问题。感谢用户通过 R bugzilla、R-devel 邮件列表、R-help 邮件列表以及私信 报告问题,在 R 4.2.0 发布后不久,这些问题已在 R 4.2.1 中得到修复。此外,好消息是,到目前为止尚未发现向 UTF-8/UCRT 过渡的重大问题。

正如 2021 年 4 月的一篇博文中所述,如果 R 社区志愿者能在发布前帮助测试 R,那就太好了。据我从我们收到错误报告的时间判断,这种情况仍然很少见。此类测试不必只是“手动”的,原则上很多交互式测试也可以自动化,但在任何情况下,都需要投入精力和时间。

剪贴板

Windows 版 R 中的剪贴板连接支持(请参阅 ?connection 并搜索“clipboard”)已在 R 4.2.0 中重写,以使用 Unicode (UTF16-LE) Windows API 接口来修复编码问题 (PR#18267)。不幸的是,在计算连接流中的偏移量时出现了一个错误,导致在连续写入期间观察到一个错误 (PR#18332),已在 R 4.2.1 中修复。这只会影响通过 R 连接 API 对剪贴板进行编程访问。

这是一对括号的尴尬遗漏,显然我当时只使用单个写入操作测试了原始错误修复,而不是多个写入操作。在修复连续写入的错误时,我还发现并修复了一个关于忽略的编码参数的虚假警告,这是连接代码内到/从 UTF16-LE 的内部转换的副产品。

出于正当理由,在自动 CRAN 包检查中不允许剪贴板连接测试(因为剪贴板是用户/系统范围的设备,与用户的 home 文件步调相同,参见 CRAN 存储库政策),因此无法通过这种方式发现问题。

传递给 C 运行时的无效参数

在发布后发现的另一个问题是 R Sys.getlocale 函数尝试在 Windows 上查询不受支持的区域设置类别。该函数记录为在 Windows 上也接受 LC_MESSAGESLC_PAPERLC_MEASUREMENT 类别,即使它们在那里不受支持;Sys.getlocale 返回一个空字符串。

该实现用于调用 C 运行时函数 setlocale 以获取区域设置信息,即使对于 LC_MESSAGES 也是如此,并且过去一直有效。但是,当启用无效参数处理程序时,它不再与 UCRT 一起使用(请参阅 MSDN 中的 参数验证)。

默认情况下,MinGW-W64 以及因此使用 Rtools42 构建的应用程序会禁用无效参数处理程序,因此我们在自动 CRAN 和 Bioconductor 包检查期间或使用“普通”版本进行手动测试时从未遇到过这种情况。但是,如果 R 嵌入在使用 Microsoft 编译器构建的应用程序中,则默认情况下可能会启用无效参数处理程序,并可能终止/使 R 崩溃。

这仅在 R 4.2.0 版本后在启用了处理程序的 RStudio 内才发现。据报道,rJava 在初始化期间崩溃,因为它使用 Sys.getlocale 查询 LC_MESSAGES 区域设置类别。

getlocale 实现已在 R 4.2.1 中修复,不再查询不受支持的区域设置类别。此外,R-devel 已扩展为可选择为检查启用这些处理程序(通过 _R_WIN_CHECK_INVALID_PARAMETERS_),并且使用此设置运行了 CRAN 包检查。幸运的是,只有少数包受到影响。一个包通过意外关闭一个句柄两次来触发无效参数处理程序,因此尝试关闭一个无效句柄。

与往常一样,检查所有 CRAN 包不仅是对包维护者的服务,而且还充当 R 本身的检查。

Rgui

也许令人惊讶的是,许多用户在 R 4.2.0 发布后在 Rgui 中发现了问题。这表明 Rgui 仍在积极使用中,不仅直接使用,还作为连接到其他应用程序并由其控制的交互式 R 控制台窗口(DasherTinn-R)。

对我来说,向 UTF-8 过渡的问题有些令人惊讶,因为 Rgui 已被设计为 Unicode 应用程序,并且使用 GraphApp 库编写以支持本机/ANSI Windows 编码中无法表示的 Unicode 字符。Rgui 在支持非 BMP 字符方面存在限制,但这并不是这里的问题。GraphApp(至少是 R 发行版中包含和定制的版本)有两种截然不同的操作模式:“Unicode”和“非 Unicode”窗口。这两种模式都支持使用本机/ANSI Windows 编码中无法表示的字符。

但是,默认情况下,“非 Unicode”窗口用于单字节区域设置(本机/ANSI),并且在某些情况下,即使在多字节区域设置中,也会因意外而使用(由于初始化/引导问题)。因此,使用单字节编码的语言的 R 用户的 Windows 系统一直使用“非 Unicode”GraphApp 窗口,并且没有发现/报告“Unicode”窗口缺少某些功能和存在一些错误。由于 R 4.2.0 切换到了 UTF-8(一种多字节区域设置),Rgui 开始使用“Unicode”GraphApp 窗口,并且出现了这些问题。这些报告来自欧洲和南美洲的用户。

其中一个后果是重音键(死键)几乎不起作用。有些根本不受支持,有些不能在不将其与下一个字符组合的情况下键入。已修复报告的案例(以及我在调试时发现的一些其他案例)。但是,至少在 GraphApp“Unicode”窗口中完成的这些字符的处理是特定于语言的,并且取决于键盘布局。因此,肯定不排除某些重音符号通过死键仍然不起作用:在这种情况下,最好的行动方案是使用复制粘贴(或特定语言中常见的其他输入方法)作为解决方法,并报告错误。作为最后的手段,字符串文字中的非 ASCII 字符可以使用 \u\U 转义来表示。

GraphApp“Unicode”窗口在内部设计不同,并响应不同的 Windows API 消息。因此,使用 Dasher 中使用的 SendInput 注入文本不起作用。幸运的是,这个问题仍然可以解决,并且在 R 4.2.1 中已得到解决。相反,Tinn-R 使用 WM_CHAR 消息,并且它们也停止工作。如果不进行更大的 GraphApp 更改,这似乎无法修复,因为“Unicode”窗口只是被设计为以不同的方式处理相关消息,但幸运的是,Tinn-R 可以切换到 SendInput,这也是一种更好的文本注入方式,尽管它也有限制(更多详细信息 在此)。如果还有其他类似的应用程序使用 WM_CHARWM_KEYDOWN/WM_KEYUP 消息,最好的/最简单的操作方案是切换到 SendInput。从长远来看,切换到嵌入可能更灵活、更可靠,但需要更高的投资。

Rgui 有一个“脚本编辑器”,它使用 RichEdit 控件(Windows 的一部分)实现。GraphApp 一直在使用控件的 ANSI(*A)接口,因此人们会期望它应该像以前使用任何 ANSI 编码(甚至是双字节)一样使用 UTF-8。然而,事实证明控件的 RichEdit20A 版本没有,无法复制和执行包含非 ASCII 字符的 R 代码行(字符以 ANSI 编码接收,不尊重 Rgui 在其清单中选择 UTF-8)。但是,控件的 RichEdit20W 版本正确地接受 UTF-8,即使使用 ANSI(*A)。如果这些方面的任何专家正在阅读这些内容,我将很乐意对当前代码进行审查或提供解释,因为这似乎没有记录。

Rgui 还经历了 txtProgressBar 的显着性能下降。进度条基于回车符和对先前状态的重复重写。Rgui 有一种不太有效的方法来实现这些:它记住该行的完整历史记录,仅在重绘时解释回车符。在重绘一行时,Rgui 会计算每个字符的宽度。因此,进度条的每次更新都会增加下次重绘时要完成的工作,并且窗口中显示的先前行也必须重绘,因此,如果多次运行进度条,性能开销会增加。

这仅在以 UTF-8 运行的 R 4.2.0 中被检测到,因为 UTF-8 是多字节区域设置,并且已使用不同的代码路径来计算字符宽度。事实证明,这段代码很早以前添加到 R 中,在缓存区域设置标识符时存在一个错误,因此它在每个字符上重新计算,另外,针对 ASCII 字符(与进度条相关)的优化意外地仅在损坏的缓存之后才发生。修复 R 中的这个旧性能错误修复了 Rgui 中的此性能下降,并且可能会在 R 构建为使用内部宽度计算的其他系统上提高性能。

其他

Rtools42 已更新,R 4.2.1 的官方版本(在撰写本文时 R-4.2.1 发布候选版本)将使用版本 5253 构建。

与用于构建 R 4.2.0 的版本 5168 相比,现在还有用于检查包中 HTML 的 tidy 工具,并且已经更新了许多库,R 本身以及所有使用这些库的 CRAN 包都会从中受益:其中 15 个由 R 和推荐的包使用,请参阅 完整列表 了解更多详情。所有 CRAN 包都针对新版本进行了测试(并在需要时更新)。请注意,当 CRAN 包可用时,CRAN 包需要使用 Rtools 中的库 CRAN 存储库策略 有更多详细信息)。

有关 R 4.2.1 中其他更新的摘要,请参阅 R-patched 分支的 NEWS 文件,并在发布前查找“R 4.2.0 patched 中的更改”或在发布后查找“R 4.2.1 中的更改”。