Redis源碼分析(二十三)——通用工具算法util

通用工具類算法是一些常見的輔助處理算法。在此主要分析正則表達式的匹配原理,以及字符串與長整型之間的相互轉換。 


正則表達式的匹配原理:

通過這次分析,纔算正真懂了正則表達式匹配的原理和具體的處理過程,也算是一大收穫。

函數stringmatchlen:

支持glob-style的通配符格式,如*表示任意一個或多個字符,?表示任意字符,[abc]表示方括號中任意一個字母。
將給定字符串與給定模式匹配,匹配返回1,否則返回0

int stringmatchlen(const char *pattern, int patternLen,
        const char *string, int stringLen, int nocase)
{
    while(patternLen) {
        switch(pattern[0]) {
        case '*':  //模式的當前字符爲*, 則只需要模式中連續*以後的部分與給定string的某後部分匹配即可
            while (pattern[1] == '*') {
                pattern++;
                patternLen--;
            }
            if (patternLen == 1)  //到此,說明模式全爲*, 當然匹配
                return 1; /* match */
            while(stringLen) {   //模式前部分全爲*, 則只需後部分與string 的某後部分匹配即可,
			                      //依次往後查找string中的該“某後部分”,直到匹配,或者到尾部
                if (stringmatchlen(pattern+1, patternLen-1,
                            string, stringLen, nocase))
                    return 1; /* match */
                string++;
                stringLen--;
            }
            return 0; /* no match */
            break;
        case '?':    //模式的當前字符爲? ,則直接跳過string的當前字符,從下一字符開始判斷
            if (stringLen == 0)
                return 0; /* no match */
            string++;
            stringLen--;
            break;
        case '[':   //模式的當前字符爲[  ,範圍,需要根據下一字符確定是爲爲 ^
        {
            int not, match;

            pattern++;
            patternLen--;
            not = pattern[0] == '^';
            if (not) {
                pattern++;
                patternLen--;
            }
            match = 0;
            while(1) {
                if (pattern[0] == '\\') {
                    pattern++;
                    patternLen--;
                    if (pattern[0] == string[0])
                        match = 1;
                } else if (pattern[0] == ']') { //[]裏面爲空
                    break;
                } else if (patternLen == 0) {
                    pattern--;
                    patternLen++;
                    break;
                } else if (pattern[1] == '-' && patternLen >= 3) {//如[1-8]:即1到8中的任意一個字符 ,只要string當前字符在此範圍內
                    int start = pattern[0];
                    int end = pattern[2];
                    int c = string[0];
                    if (start > end) {
                        int t = start;
                        start = end;
                        end = t;
                    }
                    if (nocase) {
                        start = tolower(start);
                        end = tolower(end);
                        c = tolower(c);
                    }
                    pattern += 2;
                    patternLen -= 2;
                    if (c >= start && c <= end)
                        match = 1;
                } else {
                    if (!nocase) {
                        if (pattern[0] == string[0])
                            match = 1;
                    } else {
                        if (tolower((int)pattern[0]) == tolower((int)string[0]))
                            match = 1;
                    }
                }
                pattern++;
                patternLen--;
            }
            if (not)
                match = !match;
            if (!match)
                return 0; /* no match */
            string++;
            stringLen--;
            break;
        }
        case '\\':
            if (patternLen >= 2) {
                pattern++;
                patternLen--;
            }
            /* fall through */
        default: // 如果沒有正則表達式的關鍵字符,則直接比較 
            if (!nocase) {
                if (pattern[0] != string[0])
                    return 0; /* no match */
            } else {
                if (tolower((int)pattern[0]) != tolower((int)string[0]))
                    return 0; /* no match */
            }
            string++;
            stringLen--;
            break;
        }
        pattern++;
        patternLen--;
        if (stringLen == 0) {
            while(*pattern == '*') {
                pattern++;
                patternLen--;
            }
            break;
        }
    }
    if (patternLen == 0 && stringLen == 0)//完全匹配
        return 1;
    return 0;
}


字符串與長整型之間的相互轉換:

 數值轉換爲字符串:    注意作者的具體做法,值得深思與學習!

 我們一般的做法是 每次除以10 取餘,再將餘數轉換爲對應的字符,如此從低位到高位逐位處理。
 在這作者提供了更好的做法 由於longlong類型是很長的數值,因此每次除以10,逐位處理需要進行很多次,所以採取除以100,每次處理兩位,如此一來節約了一半的時間; 另外,作者將所有的兩位數00 到99,所對應的字符全部先存放到一個字符串表中,後面只需要直接查表取出對應的字符即可,而不需要每次都去轉換。   這再次大大的提高了效率啊!

int ll2string(char* dst, size_t dstlen, long long svalue) {
//00到99的數字對應的字符串, 直接查表,而不是每次都將整數轉換爲字符串
    static const char digits[201] =
        "0001020304050607080910111213141516171819"
        "2021222324252627282930313233343536373839"
        "4041424344454647484950515253545556575859"
        "6061626364656667686970717273747576777879"
        "8081828384858687888990919293949596979899";
    int negative;
    unsigned long long value;
    uint32_t next;

    /* The main loop works with 64bit unsigned integers for simplicity, so
     * we convert the number here and remember if it is negative. */
	 //把負數轉換爲正數處理,加上一位的符號
    if (svalue < 0) {
        if (svalue != LLONG_MIN) {
            value = -svalue;
        } else {
            value = ((unsigned long long) LLONG_MAX)+1;
        }
        negative = 1;
    } else {
        value = svalue;
        negative = 0;
    }

    /* Check length. */
    uint32_t const length = digits10(value)+negative;
    if (length >= dstlen) return 0;//由於在字符串中末尾需要包含‘\0’,因此所需的字符串的長度至少要比ll長度大1

    /* Null term. */
    next = length;
    dst[next] = '\0';
    next--;   
    while (value >= 100) {   //由於每次都是處理的除以100的餘數,即ll的低兩位,因此從字符串的末尾向前開始填充
        int const i = (value % 100) * 2;
        value /= 100;
        dst[next] = digits[i + 1];//直接查表,取得對應的字符
        dst[next - 1] = digits[i];
        next -= 2;
    }

    /* Handle last 1-2 digits. */
    if (value < 10) {
        dst[next] = '0' + (uint32_t) value;
    } else {
        int i = (uint32_t) value * 2;
        dst[next] = digits[i + 1];
        dst[next - 1] = digits[i];
    }

    /* Add sign. */
	//若爲負數,在字符串開頭加上負號標誌
    if (negative) dst[0] = '-';
    return length;
}


字符串轉換爲longlong:

 //字符串轉換爲longlong:從字符串的第一位開始逐位取出,(第一位爲longlong的最高位)。每次將ll值乘以10,然後將新取出的低位作爲各位數加到ll值上
int string2ll(const char *s, size_t slen, long long *value) {
    const char *p = s;
    size_t plen = 0;
    int negative = 0;
    unsigned long long v;

    if (plen == slen)
        return 0;

    /* Special case: first and only digit is 0. */
    if (slen == 1 && p[0] == '0') {
        if (value != NULL) *value = 0;
        return 1;
    }

    if (p[0] == '-') {
        negative = 1;
        p++; plen++;

        /* Abort on only a negative sign. */
        if (plen == slen)
            return 0;
    }

    /* First digit should be 1-9, otherwise the string should just be 0. */
    if (p[0] >= '1' && p[0] <= '9') {
        v = p[0]-'0';
        p++; plen++;
    } else if (p[0] == '0' && slen == 1) {
        *value = 0;
        return 1;
    } else {
        return 0;
    }

    while (plen < slen && p[0] >= '0' && p[0] <= '9') {
        if (v > (ULLONG_MAX / 10)) /* Overflow. */
            return 0;
        v *= 10;

        if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */
            return 0;
        v += p[0]-'0';

        p++; plen++;
    }

    /* Return if not all bytes were used. */
    if (plen < slen)
        return 0;

    if (negative) {
        if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */
            return 0;
        if (value != NULL) *value = -v;
    } else {
        if (v > LLONG_MAX) /* Overflow. */
            return 0;
        if (value != NULL) *value = v;
    }
    return 1;
}



發佈了93 篇原創文章 · 獲贊 3 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章