zoukankan      html  css  js  c++  java
  • D语言中字符串的操作

           字符串的操作在软件开发中是特别重要的一个事情,因为基本上的编程都会使用到字符串,字符串操作的好坏决定着一个语言的好与差。在我做过的一个项目中曾经就出现过字符串操作性能问题。     

           D语言字符串有 string,wstring,dstring三种类型,在D语言中字符串是使用字符数组定义的,三种类型分别对应char,wchar,dchar。char只有一个字节,wchar为双字节,dchar为三字节。对字符串的操作也相当于是对数组的操作,这跟其它语言不一样,C++中字符串是以string类来进行封装,它的操作是就string类提供的函数来完成,C#中也是一样。而D中的字符更像是c语言中的字符串。

          在D语言要数组是做了很大的改进,这也使得D语言的string比其它语言的string的操作在性能上表现得更好。先来个例程看看:

    import std.stdio;
    
    int main(string[] argv)
    {
        auto tmp = "Hello Honan!";
        int n = 8;
        auto sub1 = tmp[0..$];
        auto sub2 = tmp[0..5];
        auto sub3 = tmp[0..n];
        writeln(sub1);
        writeln(sub2);
        writeln(sub3);
        readln();
        return 0;
    }

    运行输出image 
          看上面程序,auto sub1 = tmp[0..$]中,$表示数组的最大长度,这句也就是取整个字符串。

        

          这一个取子字符串的操作,这样的取字符串写法简单,而且把数组的操作直接应用到字符串操作中来,不需要另外写个subString函数来完成。在编译器层面把字符串的操作进行要处理和优化,这也可以大大提高字符串操作的性能。

          有人可能就会说,除了编译器可能会做一定优化,又能提高多少性能呢?哈哈,如果你这样想不错了,来看看下面一个例子:

    import std.stdio;
    int main(string[] argv)
    {
        auto tmp = "Hello Honan!";
        auto sub2 = tmp[0..5];
        (cast(char[])(sub2))[3] = 'K';
    
        writeln(tmp);
        writeln(sub2);
    
        readln();
        return 0;
    }

    运行输出

    image      看看这个运行结果,看见没有,原字符串和子字符串第3个字符都是K,这是个神奇的地方,因为这说明sub2这个子字符串没有产生实际的内存拷贝。这在字符串操作的时候将大大提高运行性能。这是一个很了不起的改进。

          看官在看到这句(cast(char[])(sub2))[3] = 'K'; 时可能会说,赋值一个字符都要字得这么复杂。哈哈,先不要看他复杂,这是很有用的,这种方式叫做强类型语言特征,这样做可以使得你编译时(而不是运行程序的时候)就会发现问题,不会轻易的操作字符串里面的内容,因为一般很少会直接修改字符串的内容。操作大部分都使用第三方函数来完成。

          又有人会想,那如果我的子字符串需要拷贝怎么办,这很简单,使用dup函数就可以完成:

    import std.stdio;
    int main(string[] argv)
    {
        auto tmp = "Hello Honan!";
        auto sub2 = tmp[0..5].dup;
        (cast(char[])(sub2))[3] = 'K';
    
        writeln(tmp);
        writeln(sub2);
    
        readln();
        return 0;
    }

    image auto sub2 = tmp[0..5].dup; 这一句中的dup函数就可以让字符串进行拷贝。

            让我们再来看看字符的连接操作:

    import std.stdio;
    import std.string;
    
    int main(string[] argv)
    {
        auto tmp = "Hello Honan!";
        auto tmp2 = tmp ~ " end";
    
        writeln(tmp);
        writeln(tmp2);
    
        readln();
        return 0;
    }

    image         方便吧,直接使用~运算符就可以进行。

             这几个功能远远不能字符串操作的全部功能,需要更多字符串操作功能需要导入std.string包,在这个包里包含的函数有:indexOf,lastIndexOf,lastIndexOfAny,representation,capitalize,splitLines,strip,stripLeft,stripRight,

    chomp,chompPrefix,chop,equal,leftJustify,rightJustify,center,detab,entab,translate,

    format,sformat,xformat,xsformat,inPattern,countchars,removechars,squeeze,munch,succ,tr,

    isNumeric,soundex,abbrev,column,wrap,outdent。

            这么多的字符串操作,实在是太丰富了,真是牛B人的作品呀,这么多不可能一个一个地介绍,这此函数的作在string.d里有unittest例程,看看就能明白,这里只做几个重点的函数介绍:

    image

          上图是一个string.d中的单元测试 这就是unittest语法的力量,不仅能在编程里进行测试,还能把测试代码放在程序一起,让后来的使用者很清楚地知道函数是如何使用的,真是一个牛B的发明。再去看看Go语言,真的只不过是一个玩具,到使用D语言的struct的时候,让那些使用Go的人明白D语言在那方面的表现一点也不差。这才是真正值得学习的语言,能简出,更能深入。

          还是先来看看字符串的几个重要操作吧。

         format函数

          在处理字符串时,我们经常需要使用format函数来对字符进行格式化。

    import std.stdio;
    import std.string;
    int main(string[] argv)
    {
        int age = 28;
        auto name = "Honan";
        auto str = format("Name:%s Age:%d",name,age);
    
        str.writeln();
        readln();
        return 0;
    }

    运行结果 image

    嗯,很方便,格式也和c语言中的格式一样。

         来看看format原码:

    string format(Char, Args...)(in Char[] fmt, Args args)
    {
        auto w = appender!string();
        auto n = formattedWrite(w, fmt, args);
        version (all)
        {
            // In the future, this check will be removed to increase consistency
            // with formattedWrite
            enforce(n == args.length, new FormatException(
                text("Orphan format arguments: args[", n, "..", args.length, "]")));
        }
        return w.data;
    }

          这个format函数是有一个问题的,在D语言中有三种字符串,string,wstring,dstring,format函数只支持string格式,这是很不科学的,需要改进。这样子的设计会使得非英文语系程序员很不愿意使用,因为字符串是个很关键的问题。

          值得大家注意的是,这不是编译器的问题,这是标准库的问题,也就是说标准库提供format函数时不够完善。因为对于非英文语系国家,在使用字符串时主要还是使用wstring或dstring。我将对中文字符串做详细测试。

         indexOf函数

          查找一个字节,或是一个子字符串出现的第一个位置。

    import std.stdio;
    import std.string;
    int main(string[] argv)
    {
        auto tmp = "Hello Honan! ";
        auto idx1 = tmp.indexOf('n');
        auto idx2 = tmp.indexOf("on");
    
        auto str = format("n index:%d on index:%d 
    ", idx1, idx2);
        str.writeln();
        readln();
        return 0;
    }

    image

        toLower函数:将字母转化为小字

    import std.stdio;
    import std.string;
    int main(string[] argv)
    {
        auto name = "Hello Honan! ";
        auto str = name.toLower();
        str.writeln();
        readln();
        return 0;
    }

    运行结果:

    image

    import std.stdio;
    import std.string;
    import excode;
    int main(string[] argv)
    {
        wstring name = "HELLO honan! HELLO ";
        auto str = name.toLower();
        str.UNI2GBK().writeln();
        readln();
        return 0;
    }

    image 使用全角的HELLO,也能转化为小写。

         toUpper函数:将小写转化为大写,和toLower函数一样,没什么将的了。

         count函数:数子字符串的数量

    import std.stdio;
    import std.string;
    int main(string[] argv)
    {
        auto name = "HELLO honan!";
        auto n1 = name.count('n');
        auto n2 = name.count("na");
    
        writeln(n1,",",n2);
        readln();
        return 0;
    }

    image       count与length是不是一样?有人可能会问这个问题,答案是当然不一样,count是以字符为单,比例一个汉字有在uft8格式里有3个字节,使用count的值是1,而使用length的值为3,为什么length的长度为3呢,那是因为length只是数组的长度。

    import std.stdio;
    import std.string;
    import std.conv;
    import excode;
    int main(string[] argv)
    {
        auto str = "你好!Hello!";
        auto wstr = str.to!wstring();
    
        writeln(format("str   length:%d count:%d", str.length,  str.count()));
        writeln(format("wstr  length:%d count:%d",wstr.length, wstr.count()));
        readln();
        return 0;
    }

    imageimage        因此,在我们经常需要使用中文字符串时,使用wstring的需求远远要大于使用string,因为string是uft8格式的,而uft8是变长编码,即英文字母只有一个字节,而中文汉字则有3个字节,本来string为uft8是比较好的,因为不只能使用中文,还可以使用多国文字,但这也使得string在foreach的时候不方便,没办法按字符来循环。

           而在wstring中使用的是Unicode编码,在Unicode中,length和count的值是一样的,dstring中更是一样了。

           这个count函数并不在string.h包中,这是因为D语言中具有类型函数扩展功能。这是一个很了不起的功能。

           例如:写一个函数

    int findIndex(string str,string findstr)
    {
          return indexOf(str);
    }

          那么类型string的对象就多了一个函数findIndex,使用时可以这样使用:

    auto str = "Hello Honan";
    auto idx = str.findIndex("Ho");

         这就是相当于findIndex的第一个参数的类型就是它的类型,这可以使得你在任何地方都可以给任何类型进行扩展。

         to转换函数:用于string,wstring,dstring的转换

    import std.stdio;
    import std.string;
    import std.conv;
    import excode;
    
    int main(string[] argv)
    {
        auto str = "你好!Hello!";
        auto wstr = str.to!wstring();
    
        writeln(UNI2GBK(wstr));
        readln();
        return 0;
    }

    image

    writeln函数在Windows中使用的是GBK格式,而默认auto str = “abc”的字符串是UTF8格式,如果直接使用writeln输出string和wstring是会出现乱码的,所以个人认为writeln函数做得不怎么好。只说在linux下正常,那么应该就是在windows中的编码处理得不对了,这可能跟windows默认字体的关吧,需要看看writeln函数的源代码。

            小结一下

            string :    utf8格式

            wstring : Unicode格式,即utf16

            dstring  : 四字节格式,即utf32

            这三种类型的编译,直接可以使用to函数转换。

            小问题:bug,writeln在windows下兼容性做得不好,是一个小小的bug,需要修正。

      string,wstring,dstring的静态字符串

    -------------------------------------------------------------------

            附加: 使用string,wstring,dstring的字符串初始化

           auto str = “hello”;  //string

           auto str = “hello”w;  //wstring

           auto str = “hello”d;  //dstring

    多行字符串的使用

    --------------------------------------------------------------------

    在使用字符串时,我们经常需要使用多行字符串,也可以叫文本。在C#中可以使用@””,而在D语言中也提供了比较方便的方法:

  • 相关阅读:
    Palindrome Linked List 解答
    Word Break II 解答
    Array vs Linked List
    Reverse Linked List II 解答
    Calculate Number Of Islands And Lakes 解答
    Sqrt(x) 解答
    Find Median from Data Stream 解答
    Majority Element II 解答
    Binary Search Tree DFS Template
    188. Best Time to Buy and Sell Stock IV
  • 原文地址:https://www.cnblogs.com/wanhongnan/p/5720077.html
Copyright © 2011-2022 走看看