C标准库 string.h (二)比较函数

string.h 比较函数

  • strcmp 两个字符串相比较
  • strncmp 两个字符串的前N个字节相比较
  • memcmp 两块内存的前N个字节相比较
  • strcoll 与 strxfrm 引用的函数
  • strcoll 确定两个以空字符结尾的字符串再指定区域下的词典顺序
  • strxfrm 将空字符结尾的字符串s2转换为s1处的一个(不重叠的)版本

strcmp

字符串比较函数,用于比较两个字符串的区别

#include <stdio.h>
#include <string.h>

/*
	自实现
*/
int _strcmp(const char *dest, const char *src)
{
	int res = 0;
	while( res == 0 && *src != '\0')
		res = *dest++ - *src++;
	return res;
}

/*
	标准库
*/
int strcmp(const char *s1, const char *s2)
{
	for ( ; *s1 == *s2; ++s1, ++s2 )
		if(*s1 == '\0')
			return (0);

	return ((*(unsigned char *) s1 < *(unsigned char *)s2) ? -1 : +1);
}


int main()
{
	char text[10] = "hello";

	if (_strcmp(text, "hello") == 0)
	{
		printf("it's same\n");
	}	
}

标准库实现中,先转成无符号再比较,是为了避免特殊字符出现负数,然后负数不对称的情况,所以先转成无符号再比较。博主是觉得返回1或者-1都没什么意义,所以直接返回相减的结果了。

strncmp

比较两个字符串的前n个字符

#include <stdio.h>
#include <string.h>

/*
	自实现
*/
int _strncmp(const char *s1, const char *s2, size_t n)
{
	const unsigned char *dest = s1, *src = s2;
	while( n-- > 0 && *dest != '\0')
		if (*dest++ - *src++)
			return *dest - *src;
	return 0;
}

/*
	标准库
*/
int strncmp(const char *s1, const char *s2, size_t n)
{
	for( ; 0 < n; ++s1, ++s2, --n)
		if(*s1 != *s2)
			return ((*(unsigned char *)s1 < *(unsigned char *)s2) ? -1 : +1);
		else if(*s1 == '\0')
			return 0;
	return 0;
}

int main()
{
	char hi[] = "hello world";

	if ( _strncmp( hi, "hello", 5 ) == 0) {
		printf("相等\n");
	} else {
		printf("不相等\n");
	}
}

标准库很是推崇for循环,其实也是有道理的,因为作为计数用的变量 n 以及每次都需要改变的 s1、s2 就可以很明确的放在后面了,或许这个地方for循环也有优化,不过博主还是用while循环自实现一下吧。

memcmp

#include <stdio.h>
#include <string.h>

/*
	自实现
*/
int _memcmp(const void *s1, const void *s2, size_t n)
{
	const unsigned char *dest = s1, *src = s2;
	while( n-- > 0 )
		if (*dest++ - *src++)
			return *dest - *src;
	return 0;
}

/*
	标准库
*/
int memcmp(const void *s1, const void *s2, size_t n)
{
	const unsigned char *su1, *su2;

	for(su1 = s1, su2 = su2; 0 < n; ++su1, ++su2, --n)
		if(*su1 != *su2)
			return ((*su1 < *su2) ? -1 : +1);
	return 0;
}

int main()
{
	char hi[] = "hello world";

	if ( _memcmp( hi, "hello", 5 ) == 0) {
		printf("相等\n");
	} else {
		printf("不相等\n");
	}
}

/*
	在处理一些特殊字符的时候,他们的值有可能是负数
	C语言中补码形式下,负数与正数不对称,所以使用 unsigned char是很推荐的

	memcmp 与 strncmp 的区别是 memcmp 不会检查字符串是否到结束
*/

strcoll 与 strxfrm

与 locale.h 本地库有关的字符串比较函数,在开始比较之前会按照特定的方式转换字符串然后在进行比较。

xstate.h

#ifndef _SXTATE_H_
#define _SXTATE_H_

// page 100 为了方便区域设置改变时获得操作状态表所需的信息。
/* xstate.h internal header */
	/* macros for finite state machines */
	// 有限状态机的宏

#define ST_CH		0x00ff
#define ST_STATE	0x0f00
#define ST_STOFF	8
#define ST_FOLD		0x8000
#define ST_INPUT	0x4000
#define ST_OUTPUT	0x2000
#define ST_ROTATE	0x1000
#define _NSTATE		16
	/* 类型定义 */

typedef struct{
	const unsigned short *_Tab[_NSTATE];
} _Statab;
	/* 声明*/
extern _Statab _Costate, _Mbstate, _Wcstate;

#endif

xstrxfrm.h

/*
	所有的整理函数都包含内部头文件"xstrxfrm.h"
	该头文件又包含标准头文件<string.h>和内部头文件"xstate.h"
	除此之外,"xstrxfrm.h"定义了类型 _Cosave 并且声明了函数 _Strxfrm
	一个 _Cosave 类型的数据对象在调用 _Strxfrm 之间存储状态信息
*/
#include <string.h>
#include "xstate.h"
	/* type definnitions 类型定义 */
typedef struct{
	unsigned char _State;
	unsigned short _Wchar;
} _Cosave;
	/* declarations 声明 */
size_t _Strxfrm(char *, const unsigned char **, size_t, _Cosave *);

xstrxfrm.c

#include <limits.h>
#include "xstrxfrm.h"

/* 设置默认为 0 的本地配置项 */
_Statab _Costate, _Mbstate, _Wcstate;

size_t _Strxfrm(char *sout, const unsigned char **psin,
					size_t size, _Cosave *ps) // _Cosave 存储状态信息
{
	// translate string to collatable form
	// 翻译字符串到可校对的格式
	char state = ps->_State;
	int leave = 0;
	int limit = 0;
	int nout = 0;
	const unsigned char *sin = *psin;
	unsigned short wc = ps->_Wchar; 	// 宽字节字符累加器

	for( ; ; )
	{   // perform a state transformation
		// 执行状态转换
		unsigned short code;
		const unsigned short *stab;

		if(_NSTATE <= state
			|| (stab = _Costate._Tab[state]) == NULL
			|| (_NSTATE * UCHAR_MAX) <= ++limit
			|| (code = stab[*sin] == 0) )
			break;

		state = (code & ST_STATE) >> ST_STOFF;

		if(code & ST_FOLD)
			wc = wc & ~UCHAR_MAX | code & ST_CH;
		if(code & ST_ROTATE)
			wc = wc >> CHAR_BIT & UCHAR_MAX | wc << CHAR_BIT;
		if(code & ST_OUTPUT && ((sout[nout++]
			= code & ST_CH ? code : wc) == '\0'
			|| size <= nout))
			leave = 1;
		if(code & ST_INPUT)
			if(*sin != '\0')
				++sin, limit = 0;
			else
				leave = 1;
		if(leave)
		{   // return for now
			// 现在返回
			*psin = sin;
			ps->_State = state;
			ps->_Wchar = wc;
			return nout;
		}
	}
	sout [nout++] = '\0';	// error return 错误返回
	*psin = sin;
	ps->_State = _NSTATE;
	return nout;
}

strcoll

#include <stdio.h>
#include "xstrxfrm.h"

typedef struct
{
	char buf[32];
	const unsigned char *s1, *s2, *sout;
	_Cosave state;
} Sctl;

static size_t getxfrm(Sctl *p)
{
	size_t i;

	do {
		p->sout = (const unsigned char *)p->buf;
		i = _Strxfrm(p->buf, &p->s1, sizeof (p->buf), &p->state);
		if (0 < i && p->buf[i-1] == '\0')
			return (i-1);
		else if (*p->s1 == '\0')
			p->s1 = p->s2;
	} while( i == 0 );
	return i;
}

int _strcoll(const char *s1, const char *s2)
{
	size_t n1, n2;
	Sctl st1, st2;
	static const _Cosave initial = {0};

	st1.s1 = (const unsigned char *)s1;
	st2.s2 = (const unsigned char *)s1;
	st1.state = initial;

	st2.s1 = (const unsigned char *)s2;
	st2.s2 = (const unsigned char *)s2;
	st2.state = initial;

	for (n1 = n2 = 0 ; ; )
	{
		int ans;
		size_t n;

		if (n1 == 0)
			n1 = getxfrm(&st1);
		if (n2 == 0)
			n2 = getxfrm(&st2);
		n = n1 < n2 ? n1 : n2;
		if (n == 0)
			return (n1 == n2 ? 0 : 0 < n2 ? -1 : +1);
		else if((ans = memcmp(st1.sout, st2.sout, n)) != 0)
			return ans;

		st1.sout += n, n1 -= n;
		st2.sout += n, n2 -= n;
	}
}

int main()
{
	char hi[] = "中文";

	if ( _strcoll( hi, "中文") == 0) {
		printf("相等\n");
	} else {
		printf("不相等\n");
	}
}

strxfrm

#include <stdio.h>
#include <stdlib.h>
#include "xstrxfrm.h"
#include "xstate.h"

size_t _strxfrm(char *s1, const char *s2, size_t n)
{
	/* transform s2[] to s1[] by locale-dependent rule */
	size_t nx = 0;
	const unsigned char *s = (const unsigned char *)s2;
	_Cosave state = {0};

	while(nx < n)
	{   // 转化 并 传递
		size_t i = _Strxfrm(s1, &s, n - nx, &state);

		s1 += i, nx += i;
		if(0 < i && s1[-1] =='\0')
			return nx-1;
		else if (*s == '\0')
			s = (const unsigned char *)s2;
	}
	for( ; ; )
	{
		char buf[32];
		size_t i = _Strxfrm(buf, &s, sizeof(buf), &state);

		nx += i;
		if(0<i && buf[i-1] == '\0')
			return nx-1;
		else if(*s == '\0')
			s = (const unsigned char *)s2;
	}
}

int main()
{
	char hi[] = "中文";

	if ( _strxfrm( hi, "中文", 4) == 0) {
		printf("相等\n");
	} else {
		printf("不相等\n");
	}
}

小结

其实可以发现三个cmp函数最后都是用的无符号字符型来相减的,
原因是为了避免有符号的负数运算。

接着就是 strcmp、strncmp和memcmp的区别了。。

str开头的是专门处理字符串的,如果去处理别的数据需要显示的强转成 char * 。
然后strcmp是一直比较到 s1 结束为止,strncmp 则是比较传入的参数 n 个,不过依旧保留了strcmp的特点,就是 s1 结束的话也会停止。
memcmp 与 strncmp 是非常类似的,区别可以说 memcmp 只比较 n 个不管s1有没有结尾。

strcmp 适合与比较两个可能完全相同的字符串。
strncmp 适合与比较一个字符串与另一个字符串开头的 n 个。
memcmp 适合与比较两个数据(不一定是char类型)的前 n 个字节是否相同。

strxfrm 与 strcoll 会根据 locale.h 中的 LC_COLLATE 来把字符串转换成另外一种形式,然后再比较。目前是一件比较坑的事情,以后研究 locale.h 的时候,我们还会回来的。

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>