关于C#:strncmp的正确用法


strncmp proper usage

快速背景介绍:我有一个客户端和一个服务器程序,它们通过Unix套接字相互通信。在服务器端解析收到的消息时,我试图使用strncmp来确定要采取的措施。

我遇到的问题是弄清楚strncmp的length参数要使用什么。这是有问题的原因是我的某些消息共享一个公共前缀。例如,我有一条消息" getPrimary"(使服务器以主服务器地址响应)和一条消息" getPrimaryStatus"(使服务器以主服务器状态响应)。我最初的想法是执行以下操作:

1
2
3
4
5
6
if(strncmp(message,"getPrimary",strlen("getPrimary"))==0){
    return foo;
}
else if(strncmp(message,"getPrimaryStatus",strlen("getPrimaryStatus"))==0){
    return bar;
}

问题是当我发送服务器" getPrimaryStatus"时,代码将始终返回foo,因为strncmp在字符串中检查的不够多。我可以将strlen(message)作为strncmp的length参数传递,但这似乎违背了使用strncmp的目的,即防止意外输入时发生溢出。我确实有一个静态变量,可以读取的最大消息长度,但是似乎将其传递进来,因为该长度只能确保如果消息溢出,则将影响最小化。

我已经提出了一些解决方案,但是它们不是很漂亮,所以我想知道是否存在解决该问题的通用方法。

供参考,我目前的解决方案是:
对我的if / else if语句进行排序,以使所有具有公共前缀的消息都按降序进行检查(对于以后尝试向其中添加内容的人,这似乎是一种在我的代码中抛出地雷的好方法。 )。

将具有常见前缀的邮件分组在一起,然后先查找后缀:

1
2
3
4
5
6
7
if(strncmp(message,"getPrimary",strlen("getPrimary"))==0){
    if(strncmp(message,"getPrimaryStatus",strlen("getPrimaryStatus"))==0){
        return bar;
    else
        return foo;
    }
}

但这感觉很混乱,尤其是因为我正在处理大约20种可能的消息。

创建一个包含所有可能消息的数组,向初始化序列添加一个函数,该函数将按降序对数组进行排序,并让我的代码搜索该列表中的元素,直到找到匹配项。这似乎很复杂和愚蠢。

看来这应该是一个足够普遍的问题,应该在某个地方解决该问题,但是到目前为止我还找不到任何东西。

先谢谢您的帮助!


假设message中的字符串应该以空值结尾,则在此处使用strncmp()而不是strcmp()的唯一原因是要防止它在message的末尾查找message不能为空终止。

这样,您传递给strncmp()n应该是message的接收大小,您应该知道该大小(从读取消息的read() / recv()函数的返回值)。


一种技术是首先比较最长的名称-对测试(或包含关键字的表)进行排序,以使长名称在短名称之前。但是,以您的示例为例:

1
2
GetPrimaryStatus
GetPrimary

您可能要确保GetPrimaryIgnition不被识别为GetPrimary。因此,您确实需要使用两个字符串中较长的字符串(消息或关键字)的长度进行比较。

您的数据结构可能是:

1
2
3
4
5
6
7
8
9
10
11
static const struct
{
    char   *name;
    size_t  name_len;
    int     retval;
} Messages[] =
{
    {"getPrimaryStatus", sizeof("getPrimaryStatus"), CMD_PRIMARYSTATUS },
    {"getPrimary",       sizeof("getPrimary"),       CMD_PRIMARY       },
    ...
};

然后,您可以遍历该表以查找相关命令。稍加小心,您就可以限制要查看的范围。请注意,sizeof()值在字符串末尾包含NUL。如果可以空终止消息,这将很有用

但是,通过将消息复制到某处或在原位修改消息,您可以使消息中的命令字空终止是最简单的。然后,您使用strcmp()而不是strncmp()。最短的唯一前缀查找更难编码。

查找命令字的一种可行方法是使用strcspn()-假设您的命令全部为字母或字母数字。


我感觉到您正在使用strncmp来防止缓冲区溢出,但是,该消息已被复制到内存(即消息缓冲区)中。还有,原型

1
int strncmp ( const char * str1, const char * str2, size_t num );

表示该函数没有副作用(即,它不会更改任何一个输入缓冲区),因此不存在覆盖缓冲区并更改内存的风险。 (strcpy()并非如此。)

您可以确保消息缓冲区的长度大于最长的命令字符串。这样,您可以确保始终访问自己拥有的内存。

另外,如果您坚持使用strncmp,则可以将命令列表存储在数组中,并按大小排序。您可以将每个字符串与长度相关联(可能还需要一个函数指针来执行处理程序)。

最后,您可以找到C ++所谓的map或Ruby或PHP称为关联数组的C版本。这使库可以为您高效而正确地处理if-else树。


使用strcmp,还要比较两个字符串的长度。如果长度相同,则strcmp将为您提供所需的结果。


使用strncmp来确定两个字符串是否相等的唯一安全方法是事先验证字符串长度是否相同:

1
2
3
4
5
/* len is a placeholder for whatever variable or function you use to get the length */
if ((len(a) == len(b)) && (strncmp(a, b, len(a)) == 0))
{
    /* Strings are equal */
}

否则,您将匹配比您的比较更长或更短的内容:

strncmp(a,"test", strlen("test"))匹配" testing"," test和一大堆其他字符"等。

strncmp(a,"test", strlen(a))匹配"," t"," te"," tes"。


您的消息仅包含以下命令之一,还是仅包含命令字符串,后跟空白/ open-parenthesis / etc?

如果是前者,请放下strncmp并仅使用strcmp

如果是后者,只需检查isspace(message[strlen(command)])message[strlen(command)]=='('或类似内容。 (注意:strlen(command)是一个常量,您可能应该这样写,或者使用宏从字符串文字的大小中获取它。)


不要使用strncmp()。使用strlcmp()代替。更安全


一年前从我的记忆中进行C编程时,我认为第三个参数应该告诉函数要比较的字符数。这就是安全的原因,因为您可以控制要处理的字符数

所以应该是这样的:

1
2
3
if(strncmp(message,"getPrimary", strlen("getPrimary")) {
   //
}