本文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、客户要用大写英文字母标刻流水号,且除去易混淆的字符Q、I、O等,即23进制编码流水号,且每个流水号仍然用10个字符表示。显然在这种情况下,上面的代码也没法满足客户的需求了。
系统自带的数值型变量已经不能再满足个别客户的需求了,那么我们能不能自定义一个变量类型呢,若其具有以下这样一些基本属性,和一些好用的接口,那么我们是不是也能满足客户的个性流水号了呢?
1、能设置每个流水号的宽度。
2、能设置编码符号。
3、将当前编码转换成字符串变量。
4、能与整型变量进行加减运算。(为了获取下一工件的编码)
在日常生活中我们手工记流水号时,在脑海中始终是有一个数值的,即现在是第几个工件了,然后再经过思考心算,以10进制、0123456789作为编码符,最后用笔在纸上画下一串符号。但如果让我们在手工记流水号的时候,以23进制、A-Z除去Q、I、O作为编码符记录时,对我们来说,是不是就有困难了呢,特别是对记性不好的人来说(因为要知道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类的主要目的是为了重载CString的operator[](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=
最后, 非常感谢你的耐心阅读!