简体   繁体   中英

Loading a file containing escape characters (%xx) in its name using System.Uri in C#

Edit : Rewrote the whole question to make it more clear.

I'm using the System.Windows.Media.MediaPlayer class to play mp3 files and it requires me to send in a Uri object. Most files work fine, but I've encountered a few files that happen to contain escape characters in their actual filenames. (like this: "C:\\file\\path\\some%20%E5%8D%88%E5%A4name.mp3") These fail to load because when I'm creating a System.Uri object, the contructor assumes those characters should be decoded (which they shouldn't) and automatically converts them into their unicode form (japanese characters).

Why do those files have such strange names? The files belong to another application, so I have no control over the naming. I'm simply trying to play the files.

I've tried escaping the escape characters, but when I do that, the Uri constructor skips any decoding and leaves me with an invalid file path.

It is possible avoid un-escaping certain characters using this solution , but that doesn't cover the characters in my case.

In short, I have a valid file path as a string (which returns 'true' in File.Exists(filepath)), but in creating a System.Uri object, it gets altered such that it becomes invalid. I'm trying to get it to either swallow the raw filepath somehow, or in some other way generating a System.Uri object that points to the file.

The URI just contains a sequence of octets (8-bit values).

You create a URI by passing some string of characters to some subroutine. If the URI you want to create contains character that that subroutine doesn't like, you escape the characters. The escape sequence you use gets converted to an octet, and that's what's actually in the URI.

The URI does not contain any escaped sequences! The escaping was done only to get octets into the URI that you might not be able to get in there without escaping them.

Some time later, some other routine will display the URI to you. If it sees any non-URI octets in the URI, it has to decide how to display them to you.

  • If the octets form a valid UTF8 encoding of some Unicode characters, it may decide to show you those characters.

  • Alternatively, it may decide to show the escape sequences that would generate those octets.

Either way, it's not really unencoding or unescaping anything. The URI is still whatever it was, still a sequence of octets. All that's happening is that the routine has chosen one method or the other to show you the URI in a way that you can read. The exact same URI, fed to some other routine, or even the same routine under different circumstances, might display differently.

You need to distinguish three things:

  • The way you describe the URI, in order to create it.
  • What the URI really is.
  • How the URI is being displayed to you.

They don't need to be the same.

In the case of a file: URI, there's an added wrinkle. No matter how you specify the characters in a filename, the filesystem may decide to substitute an "equivalent" sequence of characters. The Macintosh filesystem, for example, always converts filenames to "fully decomposed Unicode". If you create a file using the URL file:///some/directory/%E1, the filename on disk will actually be file:///some/directory/a\́. That is, if you try to create a file whose name contains á (LATIN SMALL LETTER A WITH ACUTE), what you actually get is a file whose name contains a (LATIN SMALL LETTER A) followed by (COMBINING ACUTE ACCENT). There's nothing you can do about that. That's just the way the filesystem works. I don't know for sure, but it might be that (HIRAGANA LETTER GE) might get fully decomposed as (HIRAGANA LETTER KE) followed by (COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK). Again, if so, there's nothing you can do about it.

I ended up copying the files to a tempfile with a name that actually works with with System.Uri when I play them. It's not an optimal solution, but it does what I need it to, so I'm going to mark this as answered.

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));
}

(Player here is an instance of System.Windows.Media.MediaPlayer.)

I'm also wiping the tempfile when it's not needed anymore.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM