正确使用strncmp
下面是快速的背景:我有一个客户端和一个服务器程序,它们通过Unix套接字相互通信。在服务器端解析接收到的消息时,我试图使用strncmp来确定要采取的操作 我遇到的问题是弄清楚strncmp的length参数到底要用什么。这个问题的原因是我的一些邮件共享一个公共前缀。例如,我有一条消息“getPrimary”,它使服务器以主服务器地址响应,还有一条消息“getPrimaryStatus”,它使服务器以主服务器的状态响应。我最初的想法是这样做:正确使用strncmp,c,C,下面是快速的背景:我有一个客户端和一个服务器程序,它们通过Unix套接字相互通信。在服务器端解析接收到的消息时,我试图使用strncmp来确定要采取的操作 我遇到的问题是弄清楚strncmp的length参数到底要用什么。这个问题的原因是我的一些邮件共享一个公共前缀。例如,我有一条消息“getPrimary”,它使服务器以主服务器地址响应,还有一条消息“getPrimaryStatus”,它使服务器以主服务器的状态响应。我最初的想法是这样做: if(strncmp(message,"getPri
if(strncmp(message,"getPrimary",strlen("getPrimary"))==0){
return foo;
}
else if(strncmp(message,"getPrimaryStatus",strlen("getPrimaryStatus"))==0){
return bar;
}
问题是,当我发送服务器“getPrimaryStatus”时,代码将始终返回foo,因为strncmp在字符串中检查得不够远。我可以将strlen(message)作为length参数传递给strncmp,但这似乎违背了使用strncmp的目的,它是为了防止意外输入时溢出。对于我可以读取的最大消息长度,我确实有一个静态变量,但似乎传入了这个变量,因为这个长度只是确保如果消息溢出,效果会最小化
我已经提出了一些解决方案,但它们不是很漂亮,所以我想知道是否有一个共同的方式来处理这个问题
作为参考,我目前的解决方案是:
对我的if/else if语句进行排序,以便按照长度递减的顺序检查任何带有公共前缀的消息(这似乎是一个很好的方法,可以在我的代码中为以后试图添加内容的人埋下一颗地雷)
将带有常用前缀的“我的邮件”分组,然后首先查找后缀:
if(strncmp(message,"getPrimary",strlen("getPrimary"))==0){
if(strncmp(message,"getPrimaryStatus",strlen("getPrimaryStatus"))==0){
return bar;
else
return foo;
}
}
但这感觉很混乱,特别是因为我正在处理大约20条不同的消息
创建一个包含所有可能消息的数组,在我的init序列中添加一个函数,该函数将按长度降序排列数组,并让我的代码搜索该列表的元素,直到找到匹配项。这似乎既复杂又愚蠢
似乎这应该是一个足够普遍的问题,应该有一个解决方案,但我还没有找到任何东西到目前为止
提前感谢您的帮助 从我一年前做C编程的记忆中挖掘出来,我认为第三个参数应该告诉函数要处理多少个字符来进行比较。这就是为什么它是安全的,因为您可以控制要处理多少个字符 所以应该是这样的:
if(strncmp(message, "getPrimary", strlen("getPrimary")) {
//
}
不要使用
strncmp()
。改用strlcmp()
。更安全。我感觉您正在使用strncmp来防止缓冲区溢出,但是,消息已经复制到内存中(即消息缓冲区)。另外,原型
int strncmp ( const char * str1, const char * str2, size_t num );
表示该函数没有副作用(即它不会更改任何一个输入缓冲区),因此不应存在覆盖缓冲区和更改内存的风险。(strcpy()的情况并非如此。)
您可以确保消息缓冲区的长度大于最长的命令字符串。这样,您就可以确保始终访问自己拥有的内存
此外,如果您坚持使用strncmp,您可以将命令列表存储在一个数组中,并将其从最大到最小排序。您可以将每个字符串与一个长度(可能还有一个执行处理程序的函数指针)相关联
最后,你可以找到C版本的C++调用地图或者露比或PHP调用关联数组的C版本。这使库能够高效、正确地处理此if-else树。您的消息是否只包含这些命令中的一个,或是后跟空格/开括号/等的命令字符串 如果是前者,请放下strncmp,只使用strcmp
如果是后者,只需检查
isspace(message[strlen(command)])
或message[strlen(command)]='('
或类似项即可。(注意:strlen(command)
是一个常量,您可能应该这样编写它,或者使用宏从字符串文本的大小获取它。)一种方法是先比较最长的名称,然后对测试(或包含关键字的表格)排序,以便名称越长越短。但是,以您的示例为例:
GetPrimaryStatus
GetPrimary
您可能希望确保GetPrimaryIgnition
不被识别为GetPrimary
。因此,您确实需要使用两个字符串中较长字符串的长度(消息或关键字)进行比较
此处的数据结构可能是:
static const struct
{
char *name;
size_t name_len;
int retval;
} Messages[] =
{
{ "getPrimaryStatus", sizeof("getPrimaryStatus"), CMD_PRIMARYSTATUS },
{ "getPrimary", sizeof("getPrimary"), CMD_PRIMARY },
...
};
然后,您可以在此表中循环查找相关命令。您可以谨慎地限制必须查看的范围。请注意,sizeof()
值包括字符串末尾的NUL。如果您可以使用null终止消息,则此选项非常有用
但是,如果您可以通过将消息复制到某个位置或在原地修改消息,以null终止消息中的命令字,这是迄今为止最简单的方法。然后使用strcmp()
而不是strncmp()
。最短的唯一前缀查找更难编码
查找命令字的一种可行方法是使用
strcspn()
——假设您的命令都是字母或字母数字。假定消息中的字符串应该以null结尾,这是在此处使用strncmp()
而不是strcmp()的唯一原因如果消息
不是以null结尾,则应防止其超出消息
的结尾
因此,传递给strncmp()
的n
应该是您应该知道的消息的接收大小(从read()
/recv()
函数的返回值
/* 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 */
}