使用 C# 中的 System.Uri 加载名称中包含转义字符 (%xx) 的文件

本文关键字:转义字符 包含 %xx 文件 中的 System Uri 加载 使用 | 更新日期: 2023-09-27 18:35:46

编辑:重写了整个问题以使其更清晰。

我正在使用System.Windows.Media.MediaPlayer类来播放mp3文件,它需要我发送一个Uri对象。大多数文件都可以正常工作,但我遇到了一些文件,它们的实际文件名中恰好包含转义字符。(像这样:"C:''file''path''some%20%E5%8D%88%E5%A4name.mp3")这些无法加载,因为当我创建 System.Uri 对象时,构造器假定这些字符应该被解码(它们不应该)并自动将它们转换为它们的 unicode 形式(日语字符)。

为什么这些文件有这么奇怪的名字?这些文件属于另一个应用程序,所以我无法控制命名。我只是想播放文件。

我尝试转义转义字符,但是当我这样做时,Uri 构造函数会跳过任何解码并给我留下无效的文件路径。

使用此解决方案可以避免取消转义某些字符,但这不包括我的情况中的字符。

简而言之,我有一个有效的文件路径作为字符串(在File.Exists(filepath)中返回"true"),但是在创建System.Uri对象时,它被更改以致无效。我试图让它以某种方式吞下原始文件路径,或者以其他方式生成指向该文件的 System.Uri 对象。

使用 C# 中的 System.Uri 加载名称中包含转义字符 (%xx) 的文件

URI 只包含一系列八位字节(8 位值)。

通过将一些字符串传递给某个子例程来创建 URI。如果要创建的 URI 包含该子例程不喜欢的字符,则对这些字符进行转义。您使用的转义序列将转换为八位字节,这就是 URI 中的实际内容。

URI 不包含任何转义序列!转义只是为了让八位字节进入 URI,如果不转义它们,您可能无法进入那里。

一段时间后,其他一些例程将向您显示 URI。如果它在 URI 中看到任何非 URI 八位字节,则必须决定如何向您显示它们。

  • 如果八位字节构成某些 Unicode 字符的有效 UTF8 编码,则它可能决定向您显示这些字符。

  • 或者,它可能决定显示将生成这些八位字节的转义序列。

无论哪种方式,它都不是真正的解码或取消逃避任何东西。URI 仍然是原来的样子,仍然是八位字节序列。所发生的一切是,例程选择了一种方法或另一种方法,以您可以读取的方式显示 URI。馈送到其他某个例程的完全相同的 URI,甚至是在不同情况下的相同例程,可能会以不同的方式显示。

您需要区分三件事:

  • 描述 URI 的方式,以便创建它。
  • URI 到底是什么。
  • 如何向您显示 URI。

它们不需要相同。

对于文件:URI,有一个额外的皱纹。无论您如何指定文件名中的字符,文件系统都可能决定替换"等效"的字符序列。例如,Macintosh文件系统总是将文件名转换为"完全分解的Unicode"。如果使用 URL file:///some/directory/%E1 创建文件,则磁盘上的文件名实际上将为 file:///some/directory/a''u0301。也就是说,如果您尝试创建一个名称包含 á(带有急性的拉丁小写字母 A)的文件,您实际得到的是一个名称包含 a(拉丁小写字母 A)后跟(组合急性重音)的文件。对此你无能为力。这就是文件系统的工作方式。我不确定,但可能是(平假名字母GE)可能会完全分解为(平假名字母KE),然后是(组合片假名-平假名语音标记)。同样,如果是这样,你对此无能为力。

我最终将文件复制到一个临时文件中,当我播放它们时,其名称实际上适用于 System.Uri。这不是一个最佳解决方案,但它可以满足我的需要,所以我将把它标记为已回答。

const String tempFilePath = "_TempAudioFile_.mp3";
// If the file doesn't exist, don't load anything
if (!File.Exists(filepath)) return;
Uri uri = new Uri(filepath);        
if (File.Exists(uri.LocalPath))
{
    // If the filename survived being turned into a uri, load the file
    Player.Open(uri);
}
else
{
    // If the uri somehow got messed up, copy the file to a temporary file first.
    File.Copy(filepath, tempFilePath, true);
    Player.Open(new Uri(tempFilePath, UriKind.Relative));
}

(这里的播放器是System.Windows.Media.MediaPlayer的一个实例。

当不再需要临时文件时,我也在擦除它。