随笔-8  评论-196  文章-0 

编写和Mono兼容.Net程序系列----最常见的不兼容场景和基本解决办法

    对于同样的任务,不同的平台通常都会有自己的做法,表现的内容通常类似,这种做法对程序员的影响是很大的。同样,即使在.Net框架下工作,程序员也同样面临挑战,好在.Net平台在设计这在早期就意识到这些问题,.Net本身就提供了解决方案,只是,平台兼容的代码要么蹩脚,有么太复杂。现在就让我们以尽可能小的的代价,以尽可能优美的编码方式提升程序的兼容性。     

  兼容性问题通常可以分为有限的场景,每一个场景中只要遵守一定的规则,就能解决问题:

  场景一,文件系统分割符的问题,Windows使用以盘符开始的文件系统,并且使用“\”作为目录分隔符,而Unix族系统使用"/"作为文件系统的开始,并且使用"/"作为目录分隔符。

    有多种方法可以解决这个问题,第一种方法,使用软实现而不是硬实现,如果程序员使用字符串相加的方式自己组建路径这属于硬实现。分隔符不同,硬实现就不能跨平台;如果使用,System.IO.Path类的Combine方法组建目录路径,那么这属于软实现,Combile使用平台特有的分隔符组建最终路径。第二种方法,使用平台分隔符变量和String.Format函数组建最终路径,这是推荐方法,因为我觉得这样更实用,System.IO.Path.Separate 常量代表了平台独立的分隔符,比如要表示当前路径下的Data/Demo.xml,你可以这样做:System.Format("{1}{0}{2}{0}{3}",System.IO.Path.Separate,System.AppDomain.CurrentDomain.BaseDirectory,Data,"Demo.xml")。有时,这仍然会带来争议,System.IO.Path下有很多常量很有用。第三种办法最简单,只要设置设置IO_MAP=ALL环境变量(针对Linux平台),Mono运行时根据这个变量决定是否要做跨平台眏射,必要的时候,Mono会自己将"\"替换成"/"。但是,一旦你使用了绝对路径或者路径中包含盘符,那么通常,程序仍是不可移植的。解决文件系统不兼容的几个法宝:使用相对路径、使用运行时侦测路径、使用Path.Combine方法、使用路径常量(System.IO.Path下)、以及终极法宝IO_MAP环境变量。

    场景二,换行符差异,Unix族使用"\n"作为换行符,windows使用"\r\n"作为换行符。说道这里,可能有朋友不同意,因为在Windows下,我们似乎从来也不使用"\r\n",事实是Windows在内部使用"\r\n",但是Windows同意"\n"语义,Windows期待我们使用"\r\n"至少记事本程序是这样的。在必要时候,Windows会自己默默的转换,这主要发生在RichTextBox控件相关的应用。因为换行符导致的任何问题,你都只需要写字板打开然后保存,问题就OK了。在处理换行符的问题上,记事本和写字板有很大的不同,写字板程序识别 回车符(ASCII=13)、换行符(ASCII=10)以及标准的"\r\n"(回车符合换行符并举),更有趣的是,如果是前两种情况,在保存的时候,系统会自动将单字符"\r"或者"\n"转化为"\r\n"。但是,对于记事本而言,记事本既不识别"\r"也不识别"\n",她只识别标准的"\r\n",这不能说明记事本不好,实际上正好相反,记事本程序被设计成能够有效的自动甄别内容编码类型的文本处理软件,因此不管是ASCII、GB2312还是UTF8,他都能准确的认出来,但是在写字板中却常常出现乱码,有时记事本自作聪明的做法也让人很无奈,特别是记事本程序有时会在文件的第一个字节处加上特殊标记,这可是苦坏了一些命令行程序,主要是不使用Windows API存取的软件(依赖Dos接口的软件),Javac有时也会受到它的影响。这是一个说明编码问题有时很棘手的例子,我设计了一个简单程序来表现记事本和写字板之间的不同:

using System;
susing System.IO;
namespace ConsoleHelper
{
    class Program
    {
        static void Main(string[] args)
        {
            using(FileStream demo=new FileStream("OK.txt",FileMode.Create)){
                for (int i = 0; i < 100; ++i)
                {
                    demo.WriteByte(Convert.ToByte('A'));
                    demo.WriteByte(10);//10 is Return's Ascii code ,13 is NewLine's Ascii code
                    demo.WriteByte(Convert.ToByte('B'));
                }
            }
        }
    }
}
编译程序,运行后 用记事本浏览->用写字板浏览->不修改保存->用记事本浏览

实验时先用10做一遍,然后换成13在再做一遍,你会发现很有趣。这是记事本和写字板的一个故事,还有很多,基本上都是由于编码问题或者程序善意却错误的推断导致的。

有些说远了,转回正题,对于跨平台代码的要求很简单:用软编码而不是硬编码,不要自己猜测换行符是"\r","\n"或者"\r\n",用System.Environment.NewLine常量代替即可。

  一点知识,希望能共同分享。

 

posted on 2009-02-08 02:09 蓝色闪光 阅读(...) 评论(...) 编辑 收藏