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语言中也提供了比较方便的方法:

原文地址:https://www.cnblogs.com/wanhongnan/p/5720077.html