📄 cftanalyser.cpp
字号:
// 自定义格式化文本代码
// 作者: Janhail Luo
// 最后整理: 2003-03-03
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "CFT.h"
#include "CFTAnalyser.h"
#define CFTIsSpace(v) (v==' '||v==0||v==9||v==10||v==13)
// 构造函数
CCFTAnalyser::CCFTAnalyser()
{
m_Root.iType = 0;
m_Root.pData = 0;
}
// 析构函数
CCFTAnalyser::~CCFTAnalyser()
{
this->Clear();
}
// 初始化,
// szString是一个以0结尾的字符串,里面保存的是需要分析的字符串
// szName是一个以0结尾的字符串,里面保存这次分析的名字
// 返回true表是分析正确,返回false表是分析错误。
bool CCFTAnalyser::Init(const char* szString, const char* szName)
{
if (szString==0)
return false;
// 清除旧的分析数据
this->Clear();
// 设置名字
m_Root.strName = szName;
// 开始分析
return Analyse(szString);
}
// 清除
void CCFTAnalyser::Clear()
{
Visitor(0, 0);
m_Root.iType = 0;
m_Root.listNode.clear();
m_Root.listParm.clear();
m_Root.pData = 0;
m_Root.strName = "";
}
// 访问所有的字节点
// iType传入一个用户定义的参数
// pParam也是传入一个用户自定义的一个指针
void CCFTAnalyser::Visitor(int iType, void* pParam)
{
for (int i=0; i<m_Root.listNode.size(); i++)
{
this->VisitItem(iType, 1, m_Root.listNode[i], pParam);
}
}
// 访问某一个节点
// iType 是用户自定义的参数
// iDepth 是这个节点在节点树中的层次,根节点的层次为0
// pItem 是当前访问的节点
// pParam 是用户自定义的一个指针
void CCFTAnalyser::VisitItem(int iType, int iDepth,
CCFTAnalyser::SItem* pItem, void* pParam)
{
// 在访问一个节点之前调用这个虚函数
this->OnVisitBefore(iType, iDepth, pItem, pParam);
// 如果这个节点是一个标记节点就说明它有子节点
if (pItem->iType==0)
{
// 访问所有的子节点
for (int i=0; i<pItem->listNode.size(); i++)
{
// 递归调用访问函数
this->VisitItem(iType, iDepth+1,
pItem->listNode[i], pParam);
}
}else
{
// 在访问一个节点的时候调用这个虚函数
this->OnVisit(iType, iDepth, pItem, pParam);
}
// 在访问一个节点之后调用这个虚函数
this->OnVisitAfter(iType, iDepth, pItem, pParam);
}
// 访问一个节点前,这是一个虚函数,用户可以自定义这个函数
// iType 是用户自定义的参数
// iDepth 是这个节点在节点树中的层次,根节点的层次为0
// pItem 是当前访问的节点
// pParam 是用户自定义的一个指针
void CCFTAnalyser::OnVisitBefore(int iType, int iDepth,
CCFTAnalyser::SItem* pItem, void* pParam)
{
map<CString, CString>::iterator it;
CString str;
switch (iType)
{
#ifdef _DEBUG
// 默认-1为Debug的输出
case -1:
if (pItem->iType==0)
{
str = "<" + pItem->strName;
it = pItem->listParm.begin();
while (it!=pItem->listParm.end())
{
str += " " + (*it).first + "=" + (*it).second;
it ++;
}
str += ">\n";
}else
{
str = pItem->strName + "\n";
}
OutputDebugString(str);
break;
#endif
// 默认 0 为清空节点内容
case 0:
break;
}
}
// 访问一个节点,这是一个虚函数,用户可以自定义这个函数
// iType 是用户自定义的参数
// iDepth 是这个节点在节点树中的层次,根节点的层次为0
// pItem 是当前访问的节点
// pParam 是用户自定义的一个指针
void CCFTAnalyser::OnVisit(int iType, int iDepth,
CCFTAnalyser::SItem* pItem, void* pParam)
{
}
// 访问一个节点后,这是一个虚函数,用户可以自定义这个函数
// iType 是用户自定义的参数
// iDepth 是这个节点在节点树中的层次,根节点的层次为0
// pItem 是当前访问的节点
// pParam 是用户自定义的一个指针
void CCFTAnalyser::OnVisitAfter(int iType, int iDepth,
CCFTAnalyser::SItem* pItem, void* pParam)
{
switch (iType)
{
#ifdef _DEBUG
// 默认-1为Debug的输出
case -1:
if (pItem->iType==0)
{
CString str;
str = "</" + pItem->strName + ">\n";
OutputDebugString(str);
}
break;
#endif
// 默认 0 为清空节点内容
case 0:
this->OnDestroyItem(pItem);
delete pItem;
break;
}
}
// 当分析程序碰到一个标记时调用这个函数
// iState 当前分析状态
// szStart 指向标记开始部分的指针
// szEnd 指向标记结束部分的指针
bool CCFTAnalyser::DoTag(int iState, char* szStart, char* szEnd)
{
SItem* pNewItem;
CString str(szStart, szEnd-szStart);
switch (iState)
{
case 1:
if (str[0]=='/')
{
// 如果是一个结束标记,并且和前面的开始标记匹配
if (strcmp(((const char*)str)+1,
m_Stack.top()->strName)==0)
{
m_Stack.pop();
}else
{
// error
return false;
}
}else
{
// 如果是一个开始标记就创造一个新的标记节点
pNewItem = new SItem;
pNewItem->iType = 0;
pNewItem->pData = 0;
pNewItem->strName = str;
this->OnCreateItem(pNewItem);
m_Stack.top()->listNode.push_back(pNewItem);
m_Stack.push(pNewItem);
}
break;
case 3:
m_Name = str;
break;
case 5:
break;
case 7:
m_Stack.top()->listParm[m_Name] = str;
break;
}
return true;
}
// 当分析程序碰到一个属性时调用这个函数
// iState 当前分析状态
// szStart 指向标记开始部分的指针
// szEnd 指向标记结束部分的指针
bool CCFTAnalyser::DoItem(int iState, char* szStart, char* szEnd)
{
CString str(szStart, szEnd-szStart);
str.TrimRight();
// 创造一个新的节点
SItem* pNewItem = new SItem;
pNewItem->iType = 1;
pNewItem->pData = 0;
pNewItem->strName = str;
this->OnCreateItem(pNewItem);
m_Stack.top()->listNode.push_back(pNewItem);
return true;
}
// 分析字符串szStr
// 如果分析成功完成就返回true
// 否则返回 false
bool CCFTAnalyser::Analyse(const char* szStr)
{
int iState; // 当前状态
// 0 自由状态
// 1 文字状态
// 2 开始一个标记'<'
// 3 一个标记中
int iTagState; // 标记状态
// 0 自由状态
// 1 名字状态
// 2 名字结束状态
// 3 属性名字状态
// 4 属性名字结束状态
// 5 赋值符号状态
// 6 赋值符号结束状态
// 7 属性值状态
bool bTagBegin; // true 是开始标记
// false 是结束标记
char* pCurChar; // 当前字符串的位置
char* pWordBegin; // 当前一段文字的开始位置
char* pWordEnd; // 当前一段文字的结束位置
// 清空堆栈的内容
while(m_Stack.size()) m_Stack.pop();
// 清除旧的所有内容
this->Clear();
// 当前位置标志为开始
pCurChar = (char*)szStr;
// 当前状态为自由
iState = 0;
// 把根元素压入堆栈
m_Stack.push(&m_Root);
while (*pCurChar)
{
switch (iState)
{
case 0: // 自由状态
if (*pCurChar=='<')
{
// 如果碰到标记开始符号'<'
// 就设置当前状态到开始一个标记的状态
iState = 2;
// 假设为开始标记
bTagBegin = true;
// 一段文字在此开始
pWordBegin = pCurChar;
// 一段文字到此结束
pWordEnd = pCurChar;
}else if (!CFTIsSpace(*pCurChar))
{
// 如果碰到一个非空格的文字
// 就表示进入文字输入状态
iState = 1;
// 一段文字在此开始
pWordBegin = pCurChar;
}
break;
case 1: // 文字状态
if (*pCurChar=='<')
{
// 如果碰到标记开始符号'<'
// 就设置当前状态到开始一个标记的状态
iState = 2;
// 假设为开始标记
bTagBegin = true;
// 一段文字到此结束
pWordEnd = pCurChar;
if (pWordEnd>pWordBegin)
{
this->DoItem(iState, pWordBegin, pWordEnd);
}
}
break;
case 2: // 开始一个标记'<'
if (*pCurChar=='/')
{
// 设置当前标记是一个结束标记
bTagBegin = false;
// 表示进入标记输入状态
iState = 3;
// 设置标志状态为输入标记名字状态
iTagState = 1;
// 一段文字在此开始
pWordBegin = pCurChar;
}else if (!CFTIsSpace(*pCurChar))
{
// 如果碰到一个非空格的文字
// 就表示进入标记输入状态
iState = 3;
// 设置标志状态为输入标记名字状态
iTagState = 1;
// 一段文字在此开始
pWordBegin = pCurChar;
}
break;
case 3: // 一个标记中
if (iTagState<10)
{
if (*pCurChar=='<')
{
// 如果碰到标记开始符号'<'
// 就说明文档格式出错
return false;
}else if (*pCurChar=='>')
{
// 如果碰到标记结束符号'>'
// 就说明这个标记结束
// 跳到结束标记状态
iState = 0;
// 一段文字到此结束
pWordEnd = pCurChar;
if (pWordEnd>pWordBegin)
{
DoTag(iTagState, pWordBegin, pWordEnd);
}
}else if (*pCurChar=='=')
{
if (iTagState==3 || iTagState==4)
{
// 一段文字到此结束
pWordEnd = pCurChar;
if (iTagState==3 && pWordEnd>pWordBegin)
{
DoTag(iTagState, pWordBegin, pWordEnd);
}
pWordBegin = pCurChar;
iTagState = 5;
continue;
}else if (iTagState==5)
{
iTagState = 6;
pWordBegin = pCurChar;
}else
{
// 其他状态出现'='都是错误
return false;
}
}else if (CFTIsSpace(*pCurChar))
{
if (iTagState==1 || iTagState==3
|| iTagState==5 || iTagState==7)
{
// 一段文字到此结束
pWordEnd = pCurChar;
if (pWordEnd>pWordBegin)
{
DoTag(iTagState, pWordBegin, pWordEnd);
}
iTagState ++;
if (iTagState==8)
iTagState = 2;
continue;
}
}else
{
if (iTagState==0 || iTagState==2
|| iTagState==4 || iTagState==6)
{
// 状态变化
iTagState += 1;
if (*pCurChar=='"')
{
// 转换到引号内状态
iTagState += 10;
pWordBegin = pCurChar+1;
}else
{
pWordBegin = pCurChar;
continue;
}
}
}
}else
{
if (*pCurChar=='"')
{
// 如果在双引号中
if (pCurChar[1]=='"')
{
// 如果下一个字符也是双引号
// 说明这个是转意字符
pCurChar ++;
}else
{
iTagState -= 10;
pWordEnd = pCurChar;
if (pWordEnd>pWordBegin)
{
DoTag(iTagState, pWordBegin, pWordEnd);
}
// 转换到引号外状态
iTagState ++;
if (iTagState==8)
iTagState = 2;
}
}
}
if (*pCurChar=='>')
{
// 一段文字在此开始
pWordBegin = pCurChar;
}
break;
}
pCurChar ++;
}
#ifdef _DEBUG
Visitor(-1, 0);
#endif
return true;
}
// 当创建一个新的节点的时候就会调用这个函数
// pItem 当前创建的节点的指针
void CCFTAnalyser::OnCreateItem(CCFTAnalyser::SItem* pItem)
{
}
// 当销毁一个节点的时候就会调用这个函数
// pItem 当前销毁的节点的指针
void CCFTAnalyser::OnDestroyItem(CCFTAnalyser::SItem* pItem)
{
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -