一个好用的编码类

本文VC6.0工程源码下载地址:

http://code.google.com/p/serial-num/downloads/detail?name=SerialClass.rar&can=2&q=

 

        大家好,我目前就职于一家工业制造公司,在工作中经常会碰到打印(激光标刻)序列号或流水号的问题,序列号就是某一数值对应的字符串编码,其具有计数功能。

举个例子,客户需要在一批工件上标刻序列号,每个序列号需要用5个字符标刻,即在第一个工件上标刻“00001”,在第二个工件上标刻“00002”,在第三个工件上标刻“00003”,后面的工件依次类推。

 

        起初,碰到这样的问题时,我都会用一个整型变量和一个字符串变量来满足客户的需求,整型变量用来计数,加工的时候就将整型变量转换成字符串变量,然后标刻,每加工完成一个工件后,整型变量自增就可以了。以下是对应的代码片段:

  

const int WIDTH = 5;

int nCount = 0;

CString strCount;

strCount.Format(_T("%0*d"), WIDTH, nCount++);

  

上面的设计思路视乎已能很好的满足客户需求了,但再想一下,要是客户的需求做了如下几种改动时,上面的设计思路是否还能满足客户的需求呢?!

1、客户要用30个字符标刻流水号,在这种情况下,流水号的最大数值已经超过了一个整型变量所能表达的范围。显然上面的代码已不能再满足客户的需求了。

2、客户要用大写英文字母标刻流水号,且除去易混淆的字符QIO等,即23进制编码流水号,且每个流水号仍然用10个字符表示。显然在这种情况下,上面的代码也没法满足客户的需求了。

  

系统自带的数值型变量已经不能再满足个别客户的需求了,那么我们能不能自定义一个变量类型呢,若其具有以下这样一些基本属性,和一些好用的接口,那么我们是不是也能满足客户的个性流水号了呢?

1、能设置每个流水号的宽度。

2、能设置编码符号。

3、将当前编码转换成字符串变量。

4、能与整型变量进行加减运算。(为了获取下一工件的编码)

  

在日常生活中我们手工记流水号时,在脑海中始终是有一个数值的,即现在是第几个工件了,然后再经过思考心算,以10进制、0123456789作为编码符,最后用笔在纸上画下一串符号。但如果让我们在手工记流水号的时候,以23进制、A-Z除去QIO作为编码符记录时,对我们来说,是不是就有困难了呢,特别是对记性不好的人来说(因为要知道n的编码符是个什么形状),但是我们仍然是可以完成的。只不过是,对记忆和运算能力的要求,比记普通流水号的要求要高些。

第一写博客啊,写作能力有限,不知道上面一段话大家能不能看明白,多包涵!

  

模拟我们的日常思维,我们就开始设计自定义变量类型吧!暂先命名为CSerialNum

1、为了CSerialNum类的设计方便,我们先设计这样一个CRightIndexString类,其继承自CString类。

CRightIndexString声明如下:

class CRightIndexString : public CString  
{
public:
	CRightIndexString();
	virtual ~CRightIndexString();

public:
	//改写了CString的这个函数
	void SetAt(int nIndex, TCHAR ch);

	//改写了CString的这个函数
	TCHAR operator[](int nIndex) const;

	//ref-counted copy from another CString
	const CString& operator=(const CString& stringSrc);
};

 

 

CRightIndexString实现如下:

CRightIndexString::CRightIndexString()
{
}

CRightIndexString::~CRightIndexString()
{
}

TCHAR CRightIndexString::operator[](int nIndex) const
{
	ASSERT(nIndex>=0 && nIndex<GetLength());
	return CString::operator[](GetLength()-nIndex-1);
}

void CRightIndexString::SetAt(int nIndex, TCHAR ch)
{
	ASSERT(nIndex>=0 && nIndex<GetLength());
	CString::SetAt(GetLength()-nIndex-1, ch);
}

const CString& CRightIndexString::operator=(const CString& stringSrc)
{
	return CString::operator=(stringSrc);
}

  

        设计CRightIndexString类的主要目的是为了重载CStringoperator[](int nIndex) SetAt(int nIndex, TCHAR ch),重载后,我们能更方便的像日常思维那样计数,即低位在后面,高位在前面。

  

2、下面是CSerialNum类的声明与实现:

CSerialNum类的声明:

CSerialNum::CSerialNum(int nSerialWidth, CString strCodeCharacter, CString strMinValue, CString strMaxValue)
{
	//判断有没有重复的编码符
	for (int n=0; n<strCodeCharacter.GetLength(); n++)
	{
		if (strCodeCharacter.Find(strCodeCharacter[n]) != strCodeCharacter.ReverseFind(strCodeCharacter[n]))
		{
			AfxMessageBox(_T("严重错误,编码符中存在重复的字符,请检查!"));
			return;
		}
	}

	//判断宽度和编码长度的有效性
	if (0>=nSerialWidth || 0>=strCodeCharacter.GetLength())
	{
		AfxMessageBox(_T("构造字符串时,宽度不能是0,且编码字符串不能为空!"));
		return;
	}

	//赋值给成员变量
	m_nSerialWidth = nSerialWidth;
	m_strCodeCharacter = strCodeCharacter;

	//设置序列号的编码范围
	SetRange(strMinValue, strMaxValue);
	
	//将当前序列号设为最小编码串
	CSerialNum::operator =(strMinValue);
}

CSerialNum::~CSerialNum()
{
}

BOOL CSerialNum::PaddingStr(CString &str)
{
#ifdef _DEBUG
	if (m_nSerialWidth < str.GetLength())
	{
		AfxMessageBox(_T("严重错误,序列号过长,无法前端补齐!"));
		return FALSE;
	}
#endif

	while (m_nSerialWidth > str.GetLength())
	{
		str = m_strCodeCharacter[0] + str;
	}
	return TRUE;
}

int CSerialNum::CompSerialNum(CString str1, CString str2)
{
	PaddingStr(str1);
	PaddingStr(str2);

	int nIndex1, nIndex2;

	for (int n=0; n<str1.GetLength(); n++)
	{
		nIndex1 = m_strCodeCharacter.Find(str1[n]);
		nIndex2 = m_strCodeCharacter.Find(str2[n]);

		if (nIndex1 > nIndex2)
		{
			return 1;
		}
		else if (nIndex2 > nIndex1)
		{
			return -1;
		}
	}
	return 0;
}

TCHAR CSerialNum::GetNextCode(TCHAR CrtCode, BOOL OUT &bCarry)
{
	int nCrtPosition = m_strCodeCharacter.Find(CrtCode);
	ASSERT(-1!=nCrtPosition);

	int nNextPosition = (nCrtPosition+1)%m_strCodeCharacter.GetLength();
	bCarry = (0==nNextPosition) ? TRUE : FALSE;
	return m_strCodeCharacter[nNextPosition];
}

BOOL CSerialNum::SetRange(CString strMinValue, CString strMaxValue)
{
	if (m_nSerialWidth < strMinValue.GetLength() || m_nSerialWidth < strMaxValue.GetLength())
	{
		AfxMessageBox(_T("设置序列号范围时,字符串过长,请检查!"));
		return FALSE;
	}

	if (0 >= strMinValue.GetLength() || 0 >= strMaxValue.GetLength())
	{
		AfxMessageBox(_T("设置序列号范围时,字符串过短,请检查!"));
		return FALSE;
	}

	if (strMinValue != strMinValue.SpanIncluding(m_strCodeCharacter) || strMaxValue != strMaxValue.SpanIncluding(m_strCodeCharacter))
	{
		AfxMessageBox(_T("设置序列号范围时,字符串含有未识别字符,请检查!"));
		return FALSE;
	}

	if (1 == CompSerialNum(strMinValue, strMaxValue))
	{
		AfxMessageBox(_T("设置序列号范围时,最小编码大于最大编码,请检查!"));
		return FALSE;
	}
	
	m_strMinValue = strMinValue;
	m_strMaxValue = strMaxValue;

	PaddingStr(m_strMinValue);
	PaddingStr(m_strMaxValue);

	return TRUE;
}

BOOL CSerialNum::operator=(const CString& stringSrc)
{
	if (m_nSerialWidth < stringSrc.GetLength() ||0>=stringSrc.GetLength())
	{
		AfxMessageBox(_T("设置当前序列号时,宽度过长或过短,请检查!"));
		return FALSE;
	}
	if (stringSrc != stringSrc.SpanIncluding(m_strCodeCharacter))
	{
		AfxMessageBox(_T("设置的序列号中含有未识别字符,请检查!"));
		return FALSE;
	}

	if (1 == CompSerialNum(stringSrc, m_strMaxValue) ||
		1 == CompSerialNum(m_strMinValue, stringSrc))
	{
		AfxMessageBox(_T("设置序列号时,超出了范围,请检查!"));
		return FALSE;
	}

	m_strCrtValue = stringSrc;
	PaddingStr(m_strCrtValue);
	return TRUE;
}


CSerialNum::operator CString() const
{
	return m_strCrtValue;
}


CString CSerialNum::GetSerialNoPadd(void)
{
	CString strRet = m_strCrtValue;

	if (0 >= strRet.GetLength())
	{
		AfxMessageBox(_T("严重错误,序列号为空!"));
	}
	
	while (strRet[0] == m_strCodeCharacter[0])
	{
		strRet.Delete(0);
	}

	return strRet;
}


const CString& CSerialNum::operator++(void)
{
	BOOL bCarry = FALSE;

	for (int n=0; n<m_strCrtValue.GetLength(); n++)
	{
		m_strCrtValue.SetAt(n, GetNextCode(m_strCrtValue[n], bCarry));
				
		//没有进位,即可结束循环
		if (!bCarry)
		{
			break;
		}

		//最高位有进位,则已经到达最大编码,复位至最小编码
		if (n == (m_strCrtValue.GetLength()-1))
		{
			m_strCrtValue = m_strMinValue;
			break;
		}
	}

	if (1 == CompSerialNum(m_strCrtValue, m_strMaxValue))
	{
		m_strCrtValue = m_strMinValue;
	}

	return m_strCrtValue;
}


const CString& CSerialNum::operator+=(int nIncrease)
{
	ASSERT(nIncrease>=0);

	while (nIncrease--)
	{
		operator ++();
	}

	return m_strCrtValue;
}

  

当然,这个类肯定还有很多可以完善的地方,还请多指教!

本文VC6.0工程源码下载地址:

http://code.google.com/p/serial-num/downloads/detail?name=SerialClass.rar&can=2&q=

最后, 非常感谢你的耐心阅读!

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值