通用工具類算法是一些常見的輔助處理算法。在此主要分析正則表達式的匹配原理,以及字符串與長整型之間的相互轉換。
正則表達式的匹配原理:
通過這次分析,纔算正真懂了正則表達式匹配的原理和具體的處理過程,也算是一大收穫。
函數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;
}