通用工具类算法是一些常见的辅助处理算法。在此主要分析正则表达式的匹配原理,以及字符串与长整型之间的相互转换。
正则表达式的匹配原理:
通过这次分析,才算正真懂了正则表达式匹配的原理和具体的处理过程,也算是一大收获。
函数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;
}