如何在Windows上将OsStr转换为&[u8]/Vec <u8>?

0xM*_*ang 9 string rust

我正在尝试将原始操作系统文件名持久存储,因此我需要获取原始字节数OsStr.

似乎可以调用as_bytes()*nix平台,但这不是在MS Windows上定义的.

是否有可移植的方式转换OsStr为字节?

use*_*342 8

要点OsStr是它的表示是特定于操作系统的。由于技术原因,实现有些复杂(@Shepmaster 的答案提供了更多细节),但您可以这样想:

  • 在 POSIX 系统上,OsStr归结为&[u8],因为 POSIX 函数接受并返回字节字符串;
  • 在 Windows 上,OsStr可以被认为是&[u16],因为 Win32 Unicode 函数接受并返回字符串作为 16 位单元的数组。

由于本机 Windows API 接受 16 位“宽字符” 1的序列,因此这就是OsStr设计存储的内容。虽然OsStr 可以将 an 转换为字节,因为任何东西都可以转换为字节,但这种表示形式没有用,因为这些字节对用户和系统都没有意义。这就是为什么OsStr不提供在 Windows 上以字节形式检索内容的方法。但是,它确实提供了对 Win32 中有用的基础值OsStr::encode_wide()的迭代。在另一个方向上,可用于从值切片创建一个。u16OsString::from_wide()OsStringu16

由您决定持久层如何处理平台之间的这种差异。Rust提供的是实现往返的OsStr必要工具,但不同平台的代码必然有所不同。例如,serde 通过有效地将as视为来解决差异enum OsString { Unix(Vec<u8>), Windows(Vec<u16>) }


1 Windows 宽字符串有时被描述为 UTF-16,因为这是它们在更高级别上的解释方式,但这并不适用于所有操作系统字符串。Windows 文件名可以包含u16有效 UTF-16 的值对,但仍然可用。这就是为什么无法通过将 Windows 字符串转换为 UTF-8 等方式将其表示为字节的原因。

  • 不是 WTF-16,而是 WTF-8,它是带有用于损坏代理对的额外序列的 UTF-8。重点是 Rust 暴露了这个事实,并且使得 API 无法使用 `OsStr` 的 16 位内部表示来实现。`OsStr` 事实上保证始终在内部通过 hacks 实现为 UTF-8(它必须是 `&amp;[u8]`,不能是 `&amp;[u16]`),否则它不能满足所需的“AsRef”实现。 (3认同)

She*_*ter 5

OsStr在 Rust 1.16 中没有定义的接口来获取Windows 上的字节。OsStr 委托给系统特定代码的实际实现。在 *nix 上,这是一个围绕Vec<u8>;的包装器。在 Windows 上,这是围绕Wtf8Buf. 虽然使用Wtf8Buf实现Vec<u8>,但不会公开该实现细节。有关 WTF-8 的更多详细信息可在其网站上找到,其中包括此引文,重点是我的:

在 Windows(在其 API 中使用可能格式错误的 UTF-16)上,Rust 标准库在内部将 WTF-8 用于操作系统字符串,但不公开 WTF-8 字节序列

“问题”在于,在不同平台上,将“字符串”传递给操作系统接口时没有统一的“字符串”概念。在 *nix 上,接口通常接受几乎类似于 UTF-8 的内容,但它们不处理嵌入的 NUL 值。在 Windows 上,这取决于您是否调用API的WA变体,尽管W强烈首选变体。

这变得更加困难,因为库也可能使用与操作系统不同的编码。如果您在 Windows 上使用在 *nix 上创建的 C 库,则尤其如此——它几乎可以保证接受伪 UTF-8 字符串,然后发生某种有损转换以调用正确的底层 API。

Rust 通过提供不透明类型OsStrOsString.


如果您需要将 an 传递OsStr给接受 UTF-8 数据的函数,则需要将其转换为 a Stringor &str,然后您就可以获取其字节。如果需要将其传递给接受 a 的函数,则LPCWSTR首先需要转换为 a Vec<u16>,然后将指向该缓冲区的指针传递给 Windows API。您可以看到Rust 本身如何做到这一点的示例