最近遇到一个特别诡异的问题,文件明明存在,但是在使用File.Exists判断时却显示为false,经过一番996,发现在获取文件绝对路径的时候,从文件的“属性”->"安全"Tab页里面复制“对象名称”时,必须从“左往右”选中复制,如果从“右往左”选中复制,则会包含一个不可见的"u202A"字符(Unicode字符,十进制数8234),从而导致错误。

问题产生


    平时在写验证代码的时候,通常会直接使用绝对路径,拷贝一个文件的包含名称的绝对路径的通常做法就是,找到该文件,然后从文件的“属性”->"安全"Tab页里面,复制“对象名称”,如下:如果你双击这个文件名,它会选中除跟盘符之外的其它字符串,我这里因为要复制所有的,所以习惯于从右往左选中,然后复制。因为在这里复制可以直接把整个路径加文件名一起复制过来,如果在文件浏览器里面的话,只能先复制文件所在路径,然后再去复制文件名拼到一起,过于麻烦。

    复制完成之后,把这个路径放到代码里,比如下面这个path1,然后使用FileStream打开,运行的时候会提示失败。 

    这个提示其实已经相当明确,就是文件路径不识别,应该整个路径里包含了一些非法的字符。但从表面上看不出来有什么问题。网上还有一些说法是,当前的运行环境没有对该文件的读取权限,要用管理员身份运行。我试了用管理员身份运行,但仍然会报上面的错,那就是说,根本不是权限问题。

    为了判断路径里包含了啥特殊奇怪的字符,我又添加了一些代码,试图打印出这个字符:

    结果显示,File.Exists表示这个文件不存在,文件名里包含了特殊的字符":",这个":"应该是根盘符后面的冒号,所以应该是其它原因。

解决方法


    根据前面的分析,一定是那个path1里面包含了奇怪的东西。在一番搜索后,看到网上有方法说把这个字符用ANSI显示,具体做法是把这个字符串复制到记事本,然后保存的时候,以ANSI格式保存,然后再打开。

    可以看到,用ANSI保存之后,在E盘根目录字符前面多了一个“?”。这个字符导致了这个文件的路径不能被识别。所以应该是之前使用文件属性里面从右向左复制文件路径出现了问题,现在我们尝试手动输入这个文件的完整路径,然后放到path2变量里。

    完全正常,这两个字符,肉眼上完全无法区别:

string path1 = @"‪E:\Change_of_SZSE_Securities_Lists_c.xls";
string path2 = @"E:\Change_of_SZSE_Securities_Lists_c.xls";

    但是,可以使用Unicode转ASCII工具将字符转为ASCII后然后比较:

    可以发现path1多了一个“‪”,也就是“u202A”。 0x202A的含义是:

Unicode Character 'LEFT-TO-RIGHT EMBEDDING' (U+202A)

    “从左到右的填充”。这是一个不可见字符,它用来指示对这个字符忽略常规的渲染方式,强制从左向右渲染

     所以解决方法是:对于上面path1,可以手动把光标插到“:"前面,按“Delete”键,删除“E”,然后删除“左双引号,然后在键盘上手动输入左单引号,然后输入““E”,就可以了。

     另外在“属性”->"安全"Tab页里面,复制“对象名称”时,不要从“右往左”选择然后复制,因为这样很容易把这个不可见的字符复制上,使用从“左往右”选择然后复制大概率没问题。如果有问题,参考前面的解决方法;再或者是分两步复制,首先在文件浏览器里面先复制文件所在的文件夹路径,然后再复制文件名称,然后拼接到一起。

不可见的LRO和RLO字符


    现在来深入了解一下什么是LRO和RLO字符,它们是特殊的不可见的Unicode字符。

  • LRO- Start of left-to-right-override,Unicode的十六进制编码为“0x202A”,对应的十进制就是“8234”,用来强制该符号之后的字符从左往右渲染。大多数语言,比如中文,英文等,都是从左向右阅读和书写的,所以默认为LRO。
  • RLO-Start of right-to-left-override,Unicode的十六进制编码为“0x202E”,对应的十进制就是“8238”,用来强制该符号之后的字符从右向左渲染。一些中东地区语言,比如阿拉伯语,波斯语,希伯来语,它们是从右向左书写和阅读的,所以默认为RLO。

    简单来说,这世界上的语言有两大类,一类是从左往右书写和阅读,比如中文,英文。还有一类语言是从右往左阅读和书写,比如阿拉伯语,波斯语,等等。所以需要一个标记来表示文本的渲染顺序,到底是向左还是向右。如果首先插入一个LRO,那么其后的字符都强制从左向右渲染,如果插入的是RLO,则其后的所有字符都强制从右向左渲染。

    我们可以打开记事本,首先输入一段字符,比如“从左往右”,紧接着换行,插入“Unicode控制字符",选择"RLO",紧接着再输入“从左往右”,可以看到,此时显示的是“右往左从”,顺序反过来了。

    在C#中,我们也可以使用Unicode字符加入到文件夹或者文件名中,比如下面这句:

private void MakeDir()
{
	Directory.CreateDirectory(@"D:\\"+"\u202E"+"右往左从");
}

    它会在D盘创建一个名为“从左往右”的文件夹。

    这种特殊的Unicode字符,还有一些好玩的地方,比如B站上这个视频神奇的Unicode字符:RLO

 

 

参考