Rust 如何从原始C字符串(CStr或*const u8)获取路径?

Rust 如何从原始C字符串(CStr或*const u8)获取路径?,rust,c-strings,ffi,Rust,C Strings,Ffi,使用C字符串作为Rust的路径最直接的方法是什么 我从FFI获得了const char*,需要在Rust中将其用作文件系统路径 我不希望在路径上强制使用UTF-8,因此不希望通过str/String进行转换 至少对于ASCII路径,它应该在Windows上工作 澄清一下:我只是将传递路径到fopen的现有C实现替换为Rust stdlib实现。对于给定的文件系统,它是有效路径还是正确编码不是我的问题,只要它不比fopen差(我知道fopen基本上在Windows上不起作用)。安全且可移植?就

使用C字符串作为Rust的路径最直接的方法是什么

我从FFI获得了
const char*
,需要在Rust中将其用作文件系统路径

  • 我不希望在路径上强制使用UTF-8,因此不希望通过
    str
    /
    String
    进行转换
  • 至少对于ASCII路径,它应该在Windows上工作

澄清一下:我只是将传递路径到
fopen
的现有C实现替换为Rust stdlib实现。对于给定的文件系统,它是有效路径还是正确编码不是我的问题,只要它不比
fopen
差(我知道
fopen
基本上在Windows上不起作用)。

安全且可移植?就我所知,没有办法。我的建议是要求UTF-8,祈祷它永远不会破裂

问题是,关于“C字符串”,你唯一能说的就是它以NUL结尾。你真的不能对它的编码方式说任何有意义的话。至少,没有任何真正的确定性

不安全和/或不可携带?如果您运行的是Linux(可能还有其他现代的*NIXen),那么您可以使用来进行转换。这只适用于假定C字符串首先是有效路径的情况。如果它来自一些字符串处理代码,而这些代码没有使用与文件系统相同的编码(现在通常是“看起来像UTF-8但可能不是UTF-8的任意字节”)。。。好吧,首先你得自己把它转换

在窗户上?哈哈哈。这取决于字符串的来源。嵌入在可执行文件中的C字符串可以采用多种编码,具体取决于代码的编译方式。如果它来自操作系统本身,它可能是两种不同编码中的一种:线程的OEM代码页或线程的ANSI代码页。我从来没有想过如何检查它的设置。如果它来自控制台,那么当您收到它时,它将以控制台的输入编码设置的任何形式出现。。。假设它不是从使用不同编码的其他东西(您好,PowerShell!)传入的。以上所有这些都要求您滚动自己的代码转换,因为Rust本身通过在Windows上从不使用非Unicode API来避免这种情况

哦,别忘了没有8位编码可以正确存储Windows路径,因为Windows路径是“看起来像UTF-16但可能不是的任意16位字”。[1]

。。。所以,就像我说的:要求UTF-8,祈祷它永远不会中断,因为试图“正确”地完成它会导致疯狂



[1] :我应该澄清:有这样一种编码:WTF-8,这就是Rust在Windows上用于
OsStr
OsString
的编码。需要注意的是,Windows上没有其他任何东西使用这种方法,因此它永远不会是C字符串的编码方式。

以下是我学到的:

  • Path
    /
    OsStr
    在Windows上始终使用WTF-8,并且在Unix上是一个不知道编码的字节包

  • 它们从不使用任何“宽”编码(如UTF-16或UCS-2)存储任何路径。OsStr的Windows唯一伪装是隐藏WTF-8编码,仅此而已

  • 它极不可能改变,因为标准库API支持从UTF-8
    &str
    创建
    Path
    OsStr
    ,而无需任何内存分配或变异(即
    as_ref()
    是受支持的,其严格的API除了指针强制转换之外,没有任何空间来实现它)

仅Unix零拷贝版本(它甚至不依赖于任何实现细节):

在Windows上,仅转换有效的UTF-8是最好的选择,而无需通过代码单元创建WTF-8
OsString

…
let str = ::std::str::from_utf8(slice.to_bytes()).expect("keep your surrogates paired");
let path: &Path = str.as_ref();

在Unix上使用with,后跟
PathBuf::from
,在Windows上使用
String
。@BurntSushi5但这将包括分配和复制字符串,对吗?我对这个问题的理解是,Kornel希望避免这种情况,而只是使用给定的c字符串(对吗?)。我期望有一个从
CStr
OsStr
的转换函数,但我找不到这样的函数:困惑:似乎没有一个无alloc的方法,这有点有趣,但反映了我们希望对字符串施加的安全性。@LukasKalbertodt不是
OsStrExt::from_bytes
这样的函数吗?可以使用
OsStrExt::from_bytes(CStr.to_bytes())
CStr
生成一个
OsStr
。这显然只适用于Unix,但这是不可避免的,因为Windows上的Rust使用了与
char*
不兼容的本机
OsStr
实现:/您不能有alloc自由和可移植性,因为您需要处理Windows路径可能是UTF-16编码的事实。您可以在Unix上使用
OsStrExt
@red75prime:免费获得alloc:但绝对不能假设所有路径都是ASCII。从技术上讲,您甚至不能假设所有可能的编码都有ASCII作为公共子集,因为在Windows上仍然有非ASCII编码可用。但是一旦你达到了这一点,你就很难继续关心了。Unix的评论太悲观了。从FFI接收的
const char*
将被传递给函数,如
fopen()
(这是任何C代码都会做的),因此OP不需要关心它的编码。在生锈的一面,这正是
OsStrExt::from_bytes
的用途。对于Windows部分,我非常同意。@user4815162342:关于这个字符串从何而来的问题没有什么,只是它是一个C字符串。据我所知,它可能来自一个用EBCDIC对所有字符串进行编码的数据库。这在理论上是可能的,但我怀疑OP会提到这一点,或者将数据称为<
…
let str = ::std::str::from_utf8(slice.to_bytes()).expect("keep your surrogates paired");
let path: &Path = str.as_ref();