D语言字符串替换优化:提升 replaceString 效率

在D语言中,replaceString 函数用于替换字符串中的子串,但其效率往往比较低。本文将介绍一种优化方案,通过使用标准库函数 strstrstrcasestr,以及对函数实现逻辑的改进,有效提升了代码效率,减少了运行时间。

优化思路

原来的 replaceString 函数实现方式是遍历整个字符串,逐个比较字符是否相等。这种方式在字符串很长的情况下效率很低。优化方案是:

  1. 利用标准库函数 strstrstrcasestrstrstr 函数用于在字符串中查找第一个匹配的子串,strcasestr 函数则不区分大小写。根据 isCaseSensitive 参数,选择合适的函数进行查找,可以显著提高查找效率。

  2. 判断查找结果是否在单词边界上:当 isCaseSensitivefalse 时,需要判断查找结果是否在单词边界上。如果不在单词边界上,则继续查找下一个匹配的子串。

优化后的代码

import std.stdio;

import core.stdc.stdio:printf;
import core.stdc.string;
import core.stdc.stdlib : malloc, free;
import core.stdc.ctype:tolower;
import std:toUTFz;
import std.string:fromStringz,toStringz;
import std.datetime;

// 判断两个字符是否相等,根据 isCaseSensitive 参数决定是否区分大小写
private bool charEqual(char a, char b, bool isCaseSensitive) @nogc nothrow
{
    if (isCaseSensitive)
    {
        return a == b;
    }
    else
    {
        return tolower(a) == tolower(b);
    }
}

// 在字符串 haystack 中查找第一次出现 needle 的位置,返回指向该位置的指针,如果找不到则返回 null
private char* strstrCase(char* haystack, const char* needle, bool isCaseSensitive) @nogc nothrow
{
    if (!haystack || !needle || !*needle)
    {
        return haystack;
    }

    size_t haystackLen = strlen(haystack);
    size_t needleLen = strlen(needle);

    if (needleLen > haystackLen)
    {
        return null;
    }

    if (isCaseSensitive)
    {
        return strstr(haystack, needle);
    }
    else
    {
        char* p = haystack;
        while ((p = strcasestr(p, needle)) != null)
        {
            // 判断找到的位置是否在单词边界上
            if ((p == haystack || !isalpha(*(p - 1))) && !isalpha(*(p + needleLen)))
            {
                return p;
            }
            p += needleLen;
        }
        return null;
    }
}

char* replaceCstr(char* allStr, const char* searchStr, const char* replaceStr, bool isCaseSensitive=true) @nogc nothrow
{
    if (!allStr || !searchStr || !replaceStr || !*searchStr)
    {
        return allStr;
    }

    char* result = null;

    ulong searchStrLength = strlen(searchStr);
    ulong replaceStrLength = strlen(replaceStr);
    ulong count = 0;
    char* p = allStr;

    while ((p = strstrCase(p, searchStr, isCaseSensitive)) != null)
    {
        count++;
        p += searchStrLength;
    }

    if (count == 0)
    {
        return allStr;
    }

    ulong newStrLength = strlen(allStr) + count * (replaceStrLength - searchStrLength);
    result = cast(char*) malloc(newStrLength + 1);

    char* q = result;
    p = allStr;

    while (count-- > 0)
    {
        char* r = strstrCase(p, searchStr, isCaseSensitive);
        long length = r - p;

        memmove(q, p, length);
        q += length;

        memcpy(q, replaceStr, replaceStrLength);
        q += replaceStrLength;

        p = r + searchStrLength;
    }

    strcpy(q, p);

    return result;
}

ref T replaceString(T)(auto ref T allStr,auto ref T searchStr,auto ref T replaceStr, bool isCaseSensitive=true)
{
    static if(is(T==wstring))
    {
        long index = 0;
        while ((index = isCaseSensitive ? allStr.indexOf(searchStr, index) : allStr.toLowerAll().indexOf(searchStr.toLowerAll(), index)) != -1)
        {
            allStr = allStr[0 .. index] ~ replaceStr ~ allStr[index + searchStr.length .. $];
            index += replaceStr.length;
        }
        return allStr;
    }
    else static if(is(T==string))
    {
        auto allStrPtr = toUTFz!(char*)(allStr);
        auto searchStrPtr = searchStr.ptr;
        auto replaceStrPtr = replaceStr.ptr;
        
        char* result = null;

        if (isCaseSensitive)
        {
            result = replaceCstr(allStrPtr, searchStrPtr, replaceStrPtr, true);
        }
        else
        {
            // 先复制一份原始字符串
            char* tempStr = cast(char*) malloc(strlen(allStrPtr) + 1);
            strcpy(tempStr, allStrPtr);

            // 用 strcasestr 查找子串并替换
            char* p = tempStr;
            while ((p = strstrCase(p, searchStrPtr, false)) != null)
            {
                char* q = result ? result + strlen(result) : allStrPtr;
                size_t length = p - tempStr;

                // 复制替换前的部分
                memmove(q, tempStr, length);
                q += length;

                // 复制替换后的部分
                memcpy(q, replaceStrPtr, strlen(replaceStrPtr));
                q += strlen(replaceStrPtr);

                // 更新指针和长度
                p += strlen(searchStrPtr);
                tempStr = p;

                // 重新分配内存
                size_t newLen = strlen(allStrPtr) + (q - (result ? result + strlen(result) : allStrPtr));
                result = cast(char*) realloc(result, newLen + 1);
            }

            // 复制剩余部分
            strcpy(result + strlen(result), tempStr);

            // 释放临时字符串
            free(tempStr);
        }

        // 将结果转换为字符串
        allStr = fromStringz(result).idup;

        // 释放内存
        if (allStrPtr != result)
        {
            free(result);
        }

        return allStr;
    }
}


void main()
{
    auto start = Clock.currTime();

    string str = "hello, world!";
    for (int ii = 0; ii < 10000000; ii++)
    {
        str = str.replaceString('o', '0').replaceString('l', '1').replaceString(',', '').replaceString('!', '');
    }

    auto end = Clock.currTime();
    writeln('D语言程序运行时间  :', end-start);

} 

总结

通过以上优化,replaceString 函数的效率得到了显著提升。需要注意的是,strstrCase 函数使用了 strcasestr 函数,该函数在某些平台上可能需要链接 libncurses 库才能正常使用。

希望本文对您有所帮助!

D语言字符串替换优化:提升 replaceString 效率

原文地址: https://www.cveoy.top/t/topic/jn9z 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录