epicsString.h当前描述了:
1) int epicsStrnRawFromEscaped(char *dst, size_t dstlen, const char *src, size_t srclen);
epicsStrnRawFromEscaped从字符串src最多复制strlen个字符到尺寸dstlen的缓存dst中,转换C风格转义序列为它们的二进制形式。一个zero字节终结输入字符串。只要dstlen非零,产生的字符串将是zero结尾的。返回值是实际写入dst的字符数目,不计算不何时或者zero终止符的字符。由于输出字符串不能长于源,src和dst指向相同缓存是合法的,并且strlen和dstlen有相同值,因而执行原地字符转换。
int epicsStrnRawFromEscaped(char *dst, size_t dstlen, const char *src, size_t srclen)
{
int rem = dstlen;
int ndst = 0;
while (srclen--) {
// 从源读取一个字符,如果源字符为0,则退出循环
int c = *src++;
#define OUT(chr) if (--rem > 0) ndst++, *dst++ = chr
if (!c) break;
input:
// 源字符是不为\,则输出计数加1,将\添加到目标缓存
if (c != '\\') {
OUT(c);
continue;
}
// 源长度变为0了或者源中当前为0,退出循环
if (!srclen-- || !(c = *src++)) break;
字符串表示的'\\a'->'\a'等
switch (c) {
case 'a': OUT('\a'); break;
case 'b': OUT('\b'); break;
case 'f': OUT('\f'); break;
case 'n': OUT('\n'); break;
case 'r': OUT('\r'); break;
case 't': OUT('\t'); break;
case 'v': OUT('\v'); break;
case '\\': OUT('\\'); break;
case '\'': OUT('\''); break;
case '\"': OUT('\"'); break;
// 把字符串形式的数值转成二进制形式的数值:'\\012' '\\120'
case '0' :case '1' :case '2' :case '3' :
case '4' :case '5' :case '6' :case '7' :
{ /* \ooo */
unsigned int u = c - '0';
if (!srclen-- || !(c = *src++)) {// 源已经到末尾了,则调至结束
OUT(u); goto done;
}
if (c < '0' || c > '7') {//不在八进制范围内
OUT(u); goto input;
}
u = u << 3 | (c - '0'); // 计数数值
if (!srclen-- || !(c = *src++)) {
OUT(u); goto done;
}
if (c < '0' || c > '7') {
OUT(u); goto input;
}
u = u << 3 | (c - '0');
if (u > 0377) {
/* Undefined behaviour! */
}
OUT(u);
}
break;
case 'x' : '\x12A'
{ /* \xXXX... */
unsigned int u = 0;
if (!srclen-- || !(c = *src++ & 0xff))
goto done;
while (isxdigit(c)) {
u = u << 4 | ((c > '9') ? toupper(c) - 'A' + 10 : c - '0');
if (u > 0xff) {
/* Undefined behaviour! */
}
if (!srclen-- || !(c = *src++ & 0xff)) {
OUT(u);
goto done;
}
}
OUT(u);
goto input;
}
default:
OUT(c);
}
#undef OUT
}
done:
if (dstlen)
*dst = '\0';
return ndst;
}
2) int epicsStrnEscapedFromRaw(char *outbuf, size_t outsize, const char *inbuf, size_t inlen);
epicsStrnEscapedFromRaw做的事情与epicsStrnRawFromEscaped相反:它尝试从字符串src中复制strlen个字符到尺寸为dstlen的缓存中,转换非打印字符为C风格的转义序列。一个zero字节将不终结这个输入字符串。只要dstlen为非零,输出字符串将是zero终结的。虽然将读取输入字符串中所有的字符,但实际写入到输出缓存的字符不多于dstlen。如果输出缓存足够大,返回值将是在它中存储的字符数目,或者如果dst=src,返回值是一个负数。由于转义结果加你个通常大于输入字符串,原位转换不被允许。在输出中使用的允许的转义字符常数:\a \b \f \n \r \t \v \\ \' \"。所有其它不可打印的字符将以格式\ooo形式显示为八进制转义,此处ooo为3位八进制数字(0-7)。不可打印字符有C运行时库函数isprint()确定。
int epicsStrnEscapedFromRaw(char *dst, size_t dstlen, const char *src,
size_t srclen)
{
int rem = dstlen;
int ndst = 0;
if (dst == src)
return -1;
while (srclen--) {
// 从源取一个字符
int c = *src++;
#define OUT(chr) ndst++; if (--rem > 0) *dst++ = chr
// 如果c为不可打印字符,则进行转义:'\a'->'\\a'
switch (c) {
case '\a': OUT('\\'); OUT('a'); break;
case '\b': OUT('\\'); OUT('b'); break;
case '\f': OUT('\\'); OUT('f'); break;
case '\n': OUT('\\'); OUT('n'); break;
case '\r': OUT('\\'); OUT('r'); break;
case '\t': OUT('\\'); OUT('t'); break;
case '\v': OUT('\\'); OUT('v'); break;
case '\\': OUT('\\'); OUT('\\'); break;
case '\'': OUT('\\'); OUT('\''); break;
case '\"': OUT('\\'); OUT('\"'); break;
default:// c为可打印字符,则直接将c输出到目标
if (isprint(c & 0xff)) {
OUT(c);
break;
}
\\ 此处为不可打印字符的数值的处理,从高位到低位,可处理的最大数值为八进制0377
\\ 转成字符串表示的数值
OUT('\\');
OUT('0' + ((c & 0300) >> 6));
OUT('0' + ((c & 0070) >> 3));
OUT('0' + (c & 0007));
}
#undef OUT
}\\ 末尾添加结束符
if (dstlen)
*dst = '\0';
return ndst;
}
3) size_t epicsStrnEscapedFromRawSize(const char *src, size_t srclen);
epicsStrnEscapedFromRawSize最多扫描可能包含不可打印字符的字符串src中strlen个字符,并且返回需要转义那个字符串的输出缓存的大小。在输出缓存中需要的进行终止的zero字节未被算入,因而由调用者允许。这个例程快于用一个0长度输出缓存调用epicsStrnEscapedFromRaw,二者应该返回相同结果。
size_t epicsStrnEscapedFromRawSize(const char *src, size_t srclen)
{
size_t ndst = srclen;
while (srclen--) {
int c = *src++;
switch (c) {
case '\a': case '\b': case '\f': case '\n':
case '\r': case '\t': case '\v': case '\\':
case '\'': case '\"':
ndst++;
break;
default:
if (!isprint(c & 0xff))
ndst += 3;
}
}
return ndst;
}
4) int epicsStrCaseCmp(const char *s1, const char *s2);
int epicsStrnCaseCmp(const char *s1, const char *s2, int n);
epicsStrCaseCmp和epicsStrnCaseCmp各种实现了strcasecmp和strncasecmp函数,它们不是在所有操作系统上能够获取的。它们操作类似strcmp和stncmp,但不区分大小写。
int epicsStrCaseCmp(const char *s1, const char *s2)
{
while (1) {
// 两个字符串中取相同索引的字符,转成大写字母后比较
int ch1 = toupper((int) *s1);
int ch2 = toupper((int) *s2);
if (ch2 == 0) return (ch1 != 0);
if (ch1 == 0) return -1;
if (ch1 < ch2) return -1;
if (ch1 > ch2) return 1;
s1++;
s2++;
}
}
/* 比较两个字符串中前len个字符 */
int epicsStrnCaseCmp(const char *s1, const char *s2, size_t len)
{
size_t i = 0;
while (i++ < len) {
int ch1 = toupper((int) *s1);
int ch2 = toupper((int) *s2);
if (ch2 == 0) return (ch1 != 0);
if (ch1 == 0) return -1;
if (ch1 < ch2) return -1;
if (ch1 > ch2) return 1;
s1++;
s2++;
}
return 0;
}
5) char *epicsStrDup(const char *s);
char * epicsStrnDup(const char *s, size_t len)
epicsStrDup实现了strup,它不是在所有操作系统上可以获取的。它位字符串分配足够的内存,复制它并且返回指向这个新副本的指针。这个指针应该最终被传递给free()。如果内存不足,调用cantProceed()。
char * epicsStrDup(const char *s)
{
return strcpy(mallocMustSucceed(strlen(s)+1, "epicsStrDup"), s);
}
char * epicsStrnDup(const char *s, size_t len)
{
char *buf = mallocMustSucceed(len + 1, "epicsStrnDup");
strncpy(buf, s, len);
buf[len] = '\0';
return buf;
}
6) int epicsStrPrintEscaped(FILE *fp, const char *s, int n);
epicsStrPrintEscaped打印输入缓存的内容,用转义序列替代不可打印字符。
int epicsStrPrintEscaped(FILE *fp, const char *s, size_t len)
{
int nout = 0;
while (len--) {
char c = *s++;
switch (c) {
case '\a': nout += fprintf(fp, "\\a"); break;
case '\b': nout += fprintf(fp, "\\b"); break;
case '\f': nout += fprintf(fp, "\\f"); break;
case '\n': nout += fprintf(fp, "\\n"); break;
case '\r': nout += fprintf(fp, "\\r"); break;
case '\t': nout += fprintf(fp, "\\t"); break;
case '\v': nout += fprintf(fp, "\\v"); break;
case '\\': nout += fprintf(fp, "\\\\"); break;
case '\'': nout += fprintf(fp, "\\'"); break;
case '\"': nout += fprintf(fp, "\\\""); break;
default:
if (isprint(0xff & (int)c))
nout += fprintf(fp, "%c", c);
else
nout += fprintf(fp, "\\%03o", (unsigned char)c);
break;
}
}
return nout;
}
7) int epicsStrGlobMatch(const char *str, const char *pattern);
epicsStrGlobMatch如果str匹配这个shell通配符模式,返回非0.
int epicsStrGlobMatch(const char *str, const char *pattern)
{
const char *cp = NULL, *mp = NULL;
// 从字符串开头和模式字符串开头逐字节依次比较,循环结束条件:遇到字符串末尾或者pattern中
// 出现了*字符
while ((*str) && (*pattern != '*')) {
// 逐字节比较,两处字符不相同并且模式串中对应字符不为?时,匹配失败,直接返回0
if ((*pattern != *str) && (*pattern != '?'))
return 0;
pattern++;//取模式串后一个字节地址
str++; // 取字符串后一个字节地址
}
// str为到结尾,初始进入这个while循环,表示当前pattern处字符一定为*
while (*str) {
if (*pattern == '*') {
if (!*++pattern)//模式字符串*之后是结束符,则表示匹配上了,返回1
return 1;
mp = pattern; // 记下*之后的字符
cp = str+1; // cp记下字符串下一个字符位置
}
else if ((*pattern == *str) || (*pattern == '?')) {
pattern++;
str++;
}
else {// 当前字符与模式匹配字符不同,并且pattern不为?也不为*
pattern = mp;
str = cp++;
}
}
while (*pattern == '*')
pattern++;
return !*pattern;
}
8) char *epicsStrtok_r(char *s, const char *delim, char **lasts);
epicsStrtok_r 实现了 strtok_r, 不是在所有操作系统上可以获取。
char * epicsStrtok_r(char *s, const char *delim, char **lasts)
{
const char *spanp;
int c, sc;
char *tok;
if (s == NULL && (s = *lasts) == NULL)
return NULL;
/*
* 跳过开头的分隔符字符串中所有分隔符
*/
cont:
c = *s++;
for (spanp = delim; (sc = *spanp++) != 0;) {
if (c == sc)
goto cont;
}
if (c == 0) { /* 已经到字符串末尾了 */
*lasts = NULL;
return NULL;
}
// 字符串中开头分隔符中的索引最大的分隔符, s是非分隔符的位置
tok = s - 1;
/*
* 扫描token(扫描分隔符:xxxaaabbb->aaabbb,x表示分隔符,s指向xxxaaabbb变为s指向aaabbb)
*/
for (;;) {
c = *s++; // 取当前字符,s指向下一个字符
spanp = delim;
do {
if ((sc = *spanp++) == c) {// 取分隔符串中所有分隔符与c比较,c是否是分隔符
if (c == 0) // 到字符串末尾了
s = NULL;
else
s[-1] = 0; //当前字符为分隔符,则当前字符替换为NULL
*lasts = s; // 记住下一次开始的位置
return tok; // 跳过开头分隔符后的第一个字符
}
} while (sc != 0);
}
}
9) unsigned int epicsStrHash(const char *str, unsigned int seed);
unsigned int epicsMemHash(const char *str, size_t length, unsigned int seed);
epicsStrHash计算一个zero终结字符串str的哈希值,而epicsMemHash对可能包含zero字节的固定长度内存缓存使用相同算法。在两种情况中,提供一个初始种子值,它使得多个字符串或者缓存被合并成单个哈希结果。最终结果应该被掩码计算来实现哈希值中所需比特数。
// 计算str字符串的哈希值
unsigned int epicsStrHash(const char *str, unsigned int seed)
{
unsigned int hash = seed;
char c;
// 逐字节计算
while ((c = *str++)) {
hash ^= ~((hash << 11) ^ c ^ (hash >> 5));
if (!(c = *str++)) break;
hash ^= (hash << 7) ^ c ^ (hash >> 3);
}
return hash;
}
// 计算str中length个字符长度的哈希值
unsigned int epicsMemHash(const char *str, size_t length, unsigned int seed)
{
unsigned int hash = seed;
while (length--) {
hash ^= ~((hash << 11) ^ *str++ ^ (hash >> 5));
if (!length--) break;
hash ^= (hash << 7) ^ *str++ ^ (hash >> 3);
}
return hash;
}
以下是一个用于演示以上库函数用法及测试的程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "epicsString.h"
int main()
{
char src[100];
char dst[100];
printf("Test epicsStrnRawFromEscaped:\n");
strcpy(src, "hello\\tworld\\n");
epicsStrnRawFromEscaped(dst, sizeof(dst),src,strlen(src));
printf("src = %s\n", src);
printf("dst = %s\n", dst);
printf("\n\n");
printf("Test epicsStrnEscapedFromRaw:\n");
strcpy(src, dst);
int srclen = strlen(src);
int dstlen = 0;
dstlen = epicsStrnEscapedFromRaw(dst, sizeof(dst), src, srclen);
printf("src = %s, len = %d\n", src, srclen);
printf("dst = %s, len = %d\n", dst, dstlen);
printf("\n\n");
printf("Test epcisStrnEscapedFromRAwSize:\n");
size_t len = epicsStrnEscapedFromRawSize(src, srclen);
printf("scr = %s, raw size: %d ---> escaped size: %ld\n", src, srclen, len);
printf("\n\n");
printf("Test epicsStrCaseCmp\n");
strcpy(src, "Compare String");
strcpy(dst, "comPare string hello");
if (epicsStrCaseCmp(src, dst) == 0){
printf("%s and %s are same (case insensitive)\n", src, dst);
}
else{
printf("%s and %s are not the same\n", src, dst);
}
printf("\n\n");
printf("Test epicsStrinCaseCmp\n");
if (epicsStrnCaseCmp(src, dst, strlen(src)) == 0){
printf("%s and %s are same before the %ld character(case insensitive)\n", src, dst, strlen(src));
}
else{
printf("%s and %s are not the same\n", src, dst);
}
printf("\n\n");
printf("Test epicsStrDup:\n");
strcpy(src, "Hello World!");
char * newchar = epicsStrDup(src);
printf("newchar = %p: %s, src = %p: %s\n", newchar, newchar, src, src);
free(newchar);
newchar = NULL;
printf("\n\n");
printf("Test epicsStrnDup:\n");
newchar = epicsStrnDup(src, 5);
printf("newchar = %p: %s, src = %p: %s\n", newchar, newchar, src, src);
free(newchar);
newchar = NULL;
printf("\n\n");
printf("Test epicsStrPrintEscaped:\n");
strcpy(src, "\t\thello\n");
epicsStrPrintEscaped(stdout, src, strlen(src));
printf("\n\n");
printf("Test epicsStrGlobMatch:\n");
strcpy(src, "Hello World");
char * patterns[4] = {"H?l*", "Hell?", "*ld", "?ell*p"};
int i;
for (i = 0; i < 4; i++){
if(epicsStrGlobMatch(src, patterns[i])){
printf("%s and %s match\n", src, patterns[i]);
}
else{
printf("%s and %s not match\n", src, patterns[i]);
}
}
printf("\n\n");
char delims[30];
printf("Test *epicsStrtok_r:\n");
strcpy(src, "Hello\tworld good,morning?Next;day\nComputer");
strcpy(dst, src);
strcpy(delims, "\t ,?;\n");
char * lasts;
char * start;
newchar = epicsStrtok_r(src, delims, &lasts);
printf("newchar = %s\n", newchar);
printf("left chars = %s\n", lasts);
while (lasts){
start = lasts;
newchar = epicsStrtok_r(start, delims, &lasts);
if (newchar){
printf("newchar = %s\n", newchar);
}
}
printf("\n\n");
printf("Test epicsStrHash:\n");
strcpy(src, "Hello World");
unsigned int hash1 = epicsStrHash(src, 1001);
unsigned int hash2 = epicsMemHash(src, 5, 1001);
printf("src = %s's hash value %u\n", src, hash1);
printf("src = %s's first 5 charachters's hash value %u\n",src, hash2);
return 0;
}
编译并且运行以上测试程序:
orangepi@orangepi5:~/epics/hostApp$ O.linux-aarch64/test_epicsString
Test epicsStrnRawFromEscaped:
src = hello\tworld\n
dst = hello world
Test epicsStrnEscapedFromRaw:
src = hello world
, len = 12
dst = hello\tworld\n, len = 14
Test epcisStrnEscapedFromRAwSize:
scr = hello world
, raw size: 12 ---> escaped size: 14
Test epicsStrCaseCmp
Compare String and comPare string hello are not the same
Test epicsStrinCaseCmp
Compare String and comPare string hello are same before the 14 character(case insensitive)
Test epicsStrDup:
newchar = 0x5579f9ef40: Hello World!, src = 0x7fe9a0c3b8: Hello World!
Test epicsStrnDup:
newchar = 0x5579f9ef40: Hello, src = 0x7fe9a0c3b8: Hello World!
Test epicsStrPrintEscaped:
\t\thello\n
Test epicsStrGlobMatch:
Hello World and H?l* match
Hello World and Hell? not match
Hello World and *ld match
Hello World and ?ell*p not match
Test *epicsStrtok_r:
newchar = Hello
left chars = world good,morning?Next;day
Computer
newchar = world
newchar = good
newchar = morning
newchar = Next
newchar = day
newchar = Computer
Test epicsStrHash and epicsMemHash:
src = Hello World's hash value 4022895612
src = Hello World's first 5 charachters's hash value 1976639928