我正在尝试将原始操作系统文件名持久化到存储器中,因此需要获取OsStr
的原始字节。
在*nix平台上似乎可以调用as_bytes()
,但在MS Windows上未定义。
是否有一种可移植的方法将OsStr
转换为字节?
我正在尝试将原始操作系统文件名持久化到存储器中,因此需要获取OsStr
的原始字节。
在*nix平台上似乎可以调用as_bytes()
,但在MS Windows上未定义。
是否有一种可移植的方法将OsStr
转换为字节?
OsStr
字节的接口。实际的OsStr
实现委托给特定于系统的代码。在*nix上,这是Vec<u8>
的包装器; 在Windows上,这是Wtf8Buf
的包装器。虽然Wtf8Buf
是用Vec<u8>
实现的,但不公开该实现细节。关于WTF-8的更多细节可在其网站上获取,其中包括以下引用,重点在于我:
在Windows(其API使用可能存在格式错误的UTF-16),Rust标准库在OS字符串上内部使用WTF-8,但不公开WTF-8字节序列。
“问题”在于,在不同平台上,当涉及传递字符串到操作系统接口时,没有统一的“字符串”概念。在*nix上,接口通常接受类似UTF-8的内容,但不处理嵌入的NUL值。在Windows上,则取决于您是调用API的W
还是A
变体,尽管强烈推荐使用W
变体。
这使得情况更加复杂,因为库也可能使用与操作系统不同的编码。如果您在Windows上使用在*nix上创建的C库,则几乎可以保证会采用伪UTF-8字符串,然后进行某种有损转换以调用正确的底层API。
Rust通过提供不透明类型OsStr
和OsString
避免所有这些问题。
OsStr
传递给一个接受UTF-8数据的函数,你需要将其转换为String
或&str
,然后获取它的字节。如果你需要将其传递给一个接受LPCWSTR
的函数,你首先需要将其转换为Vec<u16>
,然后将指向该缓冲区的指针传递给Windows API。你可以看到Rust本身是如何做到这一点的示例。OsStr
的意义在于它的表示方式与操作系统有关。为了技术原因,实现有些复杂(@Shepmaster's answer提供了更多细节),但是你可以这样想:
OsStr
化为 & [u8]
,因为 POSIX 函数接受和返回字节字符串;OsStr
可以看作是一个 & [u16]
,因为 Win32 Unicode 函数将字符串作为 16 位单位的数组接受和返回。由于本机 Windows API 接受 16 位的“宽字符”序列1,因此 OsStr
的设计目的是存储这种类型的字符。虽然 OsStr
可以转换为字节,就像任何东西都可以转换为字节一样,但这种表示对用户和系统都没有意义。这就是为什么 OsStr
在 Windows 上不提供检索内容作为字节的方法。但是,它确实提供了 OsStr::encode_wide()
,该方法迭代底层的 u16
值,在 Win32 中非常有用。在另一个方向上,可以使用 OsString::from_wide()
从 u16
值的片段创建 OsString
。
由你决定如何处理平台之间的这种差异。Rust 的 OsStr
提供了必要的工具来实现往返,但代码在不同平台上必然有所不同。例如,serde 通过有效地将作为 enum OsString { Unix(Vec<u8>), Windows(Vec<u16>) }
来解决差异。
u16
值,并仍然可用。这就是为什么不能将 Windows 字符串表示为字节,例如将其转换为 UTF-8。
OsStr
不可能是除了 UTF-8 超集以外的任何东西,因为 Rust 支持无分配零成本转换 &str
到 &Path
,以及 &Path
到 &OsStr
,所以每个有效的 &str
内存表示必须是一个有效的 &OsStr
,而 UCS-2 并不符合这一点。 - KornelOsStr
API无法实现。OsStr
被默认保证始终以UTF-8的方式进行内部实现,并使用一些技巧(必须是&[u8]
,不能是&[u16]
),否则它将无法满足所需的AsRef
实现。 - Kornel