任意文本中的合法XML串提取

Bible_Chou 2006-11-20 10:45:17
问题描述:现有一段任意文本串(ASCII格式的字符串),其中可能包含一段或者间隔的几段合法或者不合法的XML串,要求我们对这一段文本进行解析。如果其中包含合法的XML串,则以字符串的形式提取出这些XML串,如果其中不包含XML串则返回无合法XML串结果,如果其中包含非法的XML要么返回无合法XML串的结果或者指出非法XML串的位置。
输入参数:char *text(或者string);
输出结果:
1.合法的XML串:以char *(或者string)的形式输出。
2.非法的XML串:忽略或者以串的形式输出或者指出位置。

ps : 本人最近正在做一个解析器,遇到其中嵌套XML串的情况,希望朋友们能给出帮助和指点。如果分数不够可以再加。
...全文
469 21 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
Bible_Chou 2006-11-23
  • 打赏
  • 举报
回复
mLee79 2006-11-22
  • 打赏
  • 举报
回复
这咋会是例程呢, 我才写的用来提取文件里合法的 xml 片断的, 如果是自己定义的格式, 处理起来会更简单, 同时处理脚本和xml也没什么问题吧 ...

就是这个效果:

$ cat test.x; flex xml_p.l; gcc xml_p.cpp -lstdc++;echo -------- result ----------; ./a test.x
asdfasdfasdfasdfasdfasdfasdfasdfasdsf

<abc xx='yy' tt="zz" />

asdfasdfasdfasdfasdfasdfasdfasdfasdsf

<a xx='yy' tt="zz" > athsdfasdf
asdfasdf<abc>
asdfasdfasdf
</abc>
asdf;laksdjfa;sdkfl
</a>


asdfasdfasdfffffffffffffffffffffff

-------- result ----------

<abc xx='yy' tt="zz" />


<a xx='yy' tt="zz" > athsdfasdf
asdfasdf
asdf;laksdjfa;sdkfl
<abc>
asdfasdfasdf
</abc></a>
Bible_Chou 2006-11-22
  • 打赏
  • 举报
回复
首先谢谢mLee79。
上面的的例程是采用Lex来解析一段XML代码,这个在后面单独解析XML的时候会用到,不过是下一步的工作。我目前需要做的是确定我的一段脚本(在此应该理解其为一段任意文本,并非是XML)中是否存在XML代码,如果存在就提取出来,然后解析这段XML,因为存在文法冲突所以是不能同时解析脚本和XML的。现在的重点是从脚本中提取XML而非解释。
ps : 我已经采用手工查找starttag和endtag的方法基本解决了这个问题,但是存在着一些问题,所以在此希望大家能继续提出一些其他解决方案。再次谢谢大家,对于一些比较好的建议我会酌情加分的。欢迎继续讨论。
mLee79 2006-11-22
  • 打赏
  • 举报
回复
试试这个, 处理的特别粗糙了点 .....


%{
#include <iostream>
#include <string>
#include <vector>
using namespace std;

struct xNode
{
string nodeName;
string xmlCode ;
};

vector<xNode> xStack;
xNode xNodeCurr;
string xCurrValidDoc;
string xTempCtx;

#define YY_NEVER_INTERACTIVE (1)

void xError()
{
// cerr << "ERROR: " << xTempCtx << endl;
if( xCurrValidDoc != "" ) cout << "\n" << xCurrValidDoc << "\n" << endl;
xStack.clear(); xCurrValidDoc = ""; xTempCtx = "";
}

#define xONERROR xError()

#define xADDTEMP do { xTempCtx+=yytext; } while(0)

%}



WS [[:blank:]\r\n]+
OPTWS [[:blank:]\r\n]*
IDEN [[:alpha:]_\:][[:alnum:]_\-\:]*
ATTRVALUE (\"[^\"<]*\"|\'[^\'<]*\')
ATTR {IDEN}{OPTWS}"="{OPTWS}{ATTRVALUE}
NOTLT [^<]|\r|\n
ANYC .|\r|\n
VERSIONNUN \'([a-zA-Z0-9_.:]|-)+\'|\"([a-zA-Z0-9_.:]|-)+\"

%option caseless outfile="xml_p.cpp" stack nodefault noyy_top_state
%x COMMENT PLOG CDATA NODEBEGIN NODEEND

%%
<INITIAL>{
"<!--" {xADDTEMP; BEGIN(COMMENT); }
\<\?{IDEN} {xADDTEMP; BEGIN(PLOG); }
"<![CDATA[" {xADDTEMP; BEGIN(CDATA);}
"<"{IDEN} {xNodeCurr.nodeName=yytext+1;xNodeCurr.xmlCode=yytext;BEGIN(NODEBEGIN);}
"</"{IDEN} {xNodeCurr.nodeName=yytext+2;xNodeCurr.xmlCode=yytext;BEGIN(NODEEND); }
"<" {xONERROR;}
{ANYC} {xADDTEMP;}
}
<COMMENT>{
"-->" {xADDTEMP;BEGIN(INITIAL);}
{ANYC} {xADDTEMP;}
}
<PLOG>{
"?>" {xADDTEMP;BEGIN(INITIAL);}
{ANYC} {xADDTEMP;}
}
<CDATA>{
"]]>" {xADDTEMP;BEGIN(INITIAL);}
{ANYC} {xADDTEMP;}
}
<NODEBEGIN>{
{WS}|{ATTR} {xNodeCurr.xmlCode += yytext;}
"/>" {
xNodeCurr.xmlCode += yytext;
if( !xStack.size() )
cout << "\n" << xNodeCurr.xmlCode << "\n" << endl;
else if( xCurrValidDoc == "" )
xCurrValidDoc = xNodeCurr.xmlCode;

xTempCtx += xNodeCurr.xmlCode;
BEGIN(INITIAL);
}
">" {
xNodeCurr.xmlCode += yytext;
if( xStack.size() ) xStack.back().xmlCode += xTempCtx; xTempCtx = "" ;
xStack.push_back( xNodeCurr );
BEGIN(INITIAL);
}
{ANYC} {xONERROR; BEGIN(INITIAL);}
}
<NODEEND>{
{WS} {xNodeCurr.xmlCode += yytext;}
">" {
xNodeCurr.xmlCode += yytext;
if( xStack.size() ) xStack.back().xmlCode += xTempCtx; xTempCtx = "" ;
if( !xStack.size() || xStack.back().nodeName != xNodeCurr.nodeName )
{
xONERROR;
}
else
{
xCurrValidDoc = xStack.back().xmlCode + xCurrValidDoc + xNodeCurr.xmlCode;
xStack.pop_back();
}
BEGIN(INITIAL);
}
{ANYC} {xONERROR; BEGIN(INITIAL); }
}
<COMMENT,PLOG,CDATA,NODEBEGIN,NODEEND><<EOF>> {
cerr << "\nUNEXPECTED EOF detected." << endl;
xONERROR; yyterminate();
}

%%
int yywrap() { return 1; }
int main( int argc , char* argv[] )
{
if( argc != 2 || !freopen( argv[1] , "r" , stdin ) )
cerr << "usage: xx <file>" << endl , exit(-1);
yylex();
if( xCurrValidDoc != "" ) cout << "\n" << xCurrValidDoc << "\n" << endl;
return 0;
}

Bible_Chou 2006-11-22
  • 打赏
  • 举报
回复
mLee79:说得比较在理。昨天上午已经在开始手工写提取XML串的模块。其实我目前正在做的是一个脚本解释器,其中需要直接支持XML表达式XML Expr以及正则式表达式Reg Expr.
Mobidogs 2006-11-22
  • 打赏
  • 举报
回复
#include <msxml.h> 或 #include <msxml2.h>


Bible_Chou 2006-11-22
  • 打赏
  • 举报
回复
感谢mLee79。
在此有必要说明一下,我说的例程并非是说你上面的程序是从其他地方引用的已有的程序,而是说你给出的处理本问题的示例程序。
你上面的做法的确是已经达到了预期目的解析,“分离并提取XML代码”,但是由于lex冲突以及接口等原因,所以在我现在的情况下最好不要这么实现。可能我上面并没有首先说清楚这些情况,在此表示歉意。
ps : 问题我已经解决,不过还是希望能看到大家能给出的一些解决方案和建议,所以欢迎大家继续讨论。本贴打算本周末结贴,如果没有其他人提出其他的更好的方案,将给mLee79z这位朋友70%的分数(之所以70%是为了以防斑竹误以为倒分)。
mydaisy 2006-11-21
  • 打赏
  • 举报
回复
mark
Bible_Chou 2006-11-21
  • 打赏
  • 举报
回复
再顶。
ming4098 2006-11-21
  • 打赏
  • 举报
回复
mark
mLee79 2006-11-21
  • 打赏
  • 举报
回复
就是做一个有错误恢复功能的 xml parser , 感觉如果不用考虑很多细节性的问题像 dtd , entity 什么的, 也不要求非常的完善的话, 并不复杂 ....
先做一个 xml 的词法分析器, 将文件分解为 xml 预处理 <?...?> , 注释 <!--...--> , 节点开始 <nodeName(attlist)> , 节点结束 </nodeName> , <nodeName(attrList)/>等等, 这个用 lex 简单些, 手写也不麻烦 ...
然后起个 xml 解析栈, 遇节点开始压栈, 遇节点结束则与栈顶元素比较, 匹配则记录为当前最大的有效 xml 文本并出栈, 如发生错误或不匹配则输出当前最大匹配的有效 xml 文本并清空当前解析栈 直到文件结束, 这个比较简单, 没啥必要动用 yacc 啥的重型工具 ...
Bible_Chou 2006-11-21
  • 打赏
  • 举报
回复
再顶。
OOPhaisky 2006-11-20
  • 打赏
  • 举报
回复
对,用现成的xml库比较方便,但是如果楼主对xml有很深入的研究,不用也可以。
Bible_Chou 2006-11-20
  • 打赏
  • 举报
回复
需要注意的是要分析的文本是任意文本,只是其中可能包含合法或者不合法的XML串。
Bible_Chou 2006-11-20
  • 打赏
  • 举报
回复
谢谢上面的几位朋友。不过我当前要做的是判断这段文本中是否有合法的XML串,如果有则定位并且返回这XML串。
jixingzhong 2006-11-20
  • 打赏
  • 举报
回复
libxml 或者是 MSXML ,
基于现成的库,比较快捷一些 ~
jixingzhong 2006-11-20
  • 打赏
  • 举报
回复
VC使用MSXML解析XML文档

现在XML文档应用的方面特别的多.

我把以前写过的一个例子帖出来,以备以后使用.

第一部分:DOM解析:

概述:DOM解析将会把一个完整的XML文档读进来,生成一个结构树。这样会要把XML文档全部都加载到内在中。所以解析起来的速度会要慢一些。

1、如何加载xml文件:

//创建DOM,加载XML文档
MSXML::IXMLDOMDocumentPtr pCommandDoc;
pCommandDoc.CreateInstance(__uuidof(MSXML::DOMDocument));
pCommandDoc->put_async(VARIANT_FALSE);
pCommandDoc->put_validateOnParse(VARIANT_FALSE);
pCommandDoc->put_resolveExternals(VARIANT_FALSE);
pCommandDoc->put_preserveWhiteSpace(VARIANT_TRUE);
pCommandDoc->load(file.GetBuffer(0));

2、在XML文档中查找指定的结点:

//找到
MSXML::IXMLDOMNodePtr pRootNode=pCommandDoc->selectSingleNode("root/record");
if (pRootNode==NULL)
{
return ;
}

3、得到XML文档中,结点的属性

CString strTemp;
MSXML::IXMLDOMNamedNodeMapPtr pAttrs = NULL;
pRootNode->get_attributes(&pAttrs);
if (pAttrs==NULL)
{
return;
}
MSXML::IXMLDOMNodePtr pRequestTypeAttr=pAttrs->getQualifiedItem("name","");
_bstr_t strRequestType=pRequestTypeAttr->Gettext();
strTemp=strRequestType.operator char *();

4、得到结点的内容

_bstr_t strVisiPort=pNode->Gettext();

5、设置结点的内容

HRESULT hr=pNode->put_text(_bstr_t(m_strGatewayPassword));


6、设置一个属性内容
IXMLDOMAttribute *pa=NULL;
bstr = SysAllocString(L"属性1");
pXMLDom->createAttribute(bstr,&pNode);
var = VariantString(L"strin");
pa->put_value(var);
pRoot->setAttributeNode(pa, &pa1);

第二部分、如何使用SAX解析

概述:SAX使用的是加载式的,将会把XML文档分断,加载到内存中。使用事件通知的方式,来表示找到结点。好像没有写文档的能力吧。它的速度要比DOM快不少。

使用SAX的时候,就需要重载MSXML4.0中的一个接口ISAXContentHandler。

有几个函数重载了之后,当找到了结点之后,就会回调这一些函数。
jixingzhong 2006-11-20
  • 打赏
  • 举报
回复
写Unmanaged Code在.NET时代成为一种很悲惨的事,当你需要处理XML文件时,这种感觉会变得尤其强烈。FCL中的System.Xml多简单啊,连Steve Ballmer都知道怎么用。
事情不会总是那么理想的,如果你要在C/C++程序里处理XML怎么办呢?

选择一:市面上的XML lib还是有几个的,最有名的当然是libxml。我一年前用过,很不错,我还特意写了一份简明教程,后来不知搁哪儿了。

选择二:MS的MSXML,我要介绍的就是这个。

先说一下在MSDN哪里找文档吧,往下看的时候也好有个参考:在Index里打:Windows Media Services 9 Series SDK=>Programming Reference=>Programming Reference (C++)=>XML DOM Interfaces (C++)。什么?Windows Media?呵呵,不错,我觉得这个guide反而是最清楚的,你直接找MSXML,得到的结果,我觉得还没这个好。

在C程序里调用MSXML基本就是一堆COM接口,不过在Visual Studio里操作先要做点简单的设置:

在你的Project里Add References=>COM标签=>Microsoft XML v4.0,5.0其实也有了,但因为是和Office一起发布的,觉得有点怪,不想用,反正也未必用什么很怪异的功能,4.0可以了。



然后在加入这两行:

#include <msxml2.h>
#import <msxml4.dll>

头文件和dll库。什么?在哪里加?头文件或者c/cpp文件啊,哪里合适放哪儿。

然后就开始编程了,先定义两个必用的变量:

IXMLDOMDocumentPtr xmlFile = NULL;
IXMLDOMElement* xmlRoot = NULL;

为什么是必用的?  汗...

第一步当然是初始化COM:

if(FAILED(CoInitialize(NULL))) ....

接下来初始化xmlFile对象:

if(FAILED(xmlFile.CreateInstance("Msxml2.DOMDocument.4.0"))) ...

然后就可以加载xml文件了:

_variant_t varXml(L"C:\\test.xml"); //L for unicode
VARIANT_BOOL varOut;
xmlFile->load(varXml, &varOut);

取得root element:

xmlFile->get_documentElement(&xmlRoot))

取得第一级element:

IXMLDOMNodeList* xmlChildNodes = NULL;
xmlRoot->get_childNodes(&xmlChildNodes);

遍历所有第一级element:

IXMLDOMNode* currentNode = NULL;
while(!FAILED(xmlChildNodes->nextNode(¤tNode)) && currentNode != NULL)
{
//do something
}

取得当前element的名称:

BSTR nodeName;
currentNode->get_nodeName(&nodeName);

取得当前element的一个attribute(假设这个attribute叫type)的值:

IXMLDOMNamedNodeMap* attributes = NULL;
IXMLDOMNode* attributeName = NULL;
_bstr_t bstrAttributeName = "type";
BSTR nameVal;
currentNode->get_attributes(&attributes);
attributes->getNamedItem(bstrAttributeName, &attributeName);
attributeName->get_text(&nameVal);

需要注意的是,你要记住释放所有的借口,IXMLDOM***->Release(),这可不是.NET,有人帮你GC,你得自己调用Release()来减reference count,it's COM, remember?

好了,大致就这样,顺便提一下XPath:

_bstr_t bstrXmlQuery = L"/books/book[@type=scifi and @author=fox]";
IXMLDOMNodeList* nodes = NULL;
if(FAILED(xmlRoot->selectNodes(bstrXmlQuery, &nodes)) || FAILED(nodes->get_length(&length)) || length == 0)
//no match found or something went wrong
else
//match found

上面是找这样的node:

<books>
<book type="scifi" author="fox">....
</book>
....
</books>

具体的XPath语法就查手册吧,到处都有。

哦,对了,忘了说:如果你全部用ATL的类的话,借口的调用会简单一点,不过很容易转换的,比如:

IXMLDOMDocument* 对应 IXMLDOMDocumentPtr(我这里用了),其他基本也是加个Ptr,我不废话了。

最后提供一个sample,我临时攒的。工作的时候写的程序当然不能拿来贴的,呵呵。这个sample基本就是遍历整个xml,然后报告一遍文件的结构,对每个node,如果它有一个叫id的attribute,就同时打印id的值。If you want the complete VS project, shoot me an email. But I guess no one really needs it anyway, right, : )

#include "stdafx.h"
#include <windows.h>
#include <msxml2.h>
#import <msxml4.dll>

HANDLE logFile = NULL;

#define INDENT 4

#define TESTHR(hr) \
{ \
if(FAILED(hr)) goto fail; \
}

void PrintChild(IXMLDOMNodeList* nodeList, int level)
{
if(nodeList == NULL)
return;

IXMLDOMNode* currentNode = NULL;
IXMLDOMNodeList* childNodes = NULL;
IXMLDOMNamedNodeMap* attributes = NULL;
IXMLDOMNode* attributeID = NULL;

while(!FAILED(nodeList->nextNode(¤tNode)) && currentNode != NULL)
{
BSTR nodeName;
TESTHR(currentNode->get_nodeName(&nodeName));
DWORD dwBytesWritten;
for(int i=0; i<level*INDENT; i++)
WriteFile(logFile, L" ", (DWORD)(sizeof(WCHAR)), &dwBytesWritten, NULL);

//WCHAR msg[MAX_SIZE];
//wsprintf(msg, L"%s ", nodeName);
WriteFile(logFile, nodeName, (DWORD)(wcslen(nodeName)*sizeof(WCHAR)), &dwBytesWritten, NULL);

TESTHR(currentNode->get_attributes(&attributes));
if(attributes!=NULL)
{
_bstr_t bstrAttributeName = "id";
BSTR idVal;
TESTHR(attributes->getNamedItem(bstrAttributeName, &attributeID));
if(attributeID != NULL)
{
TESTHR(attributeID->get_text(&idVal));
WriteFile(logFile, L" ", (DWORD)(sizeof(WCHAR)), &dwBytesWritten, NULL);
WriteFile(logFile, idVal, (DWORD)(wcslen(idVal)*sizeof(WCHAR)), &dwBytesWritten, NULL);
WriteFile(logFile, L"\r\n", (DWORD)(2*sizeof(WCHAR)), &dwBytesWritten, NULL);
attributeID->Release(); attributeID = NULL;
}
else
{
WriteFile(logFile, L"\r\n", (DWORD)(2*sizeof(WCHAR)), &dwBytesWritten, NULL);
}
attributes->Release(); attributes = NULL;

}
else
{
WriteFile(logFile, L"\r\n", (DWORD)(2*sizeof(WCHAR)), &dwBytesWritten, NULL);
}

TESTHR(currentNode->get_childNodes(&childNodes));
PrintChild(childNodes, level+1);
currentNode=NULL;
}

fail:
if(childNodes!=NULL)
childNodes->Release();
if(attributeID!=NULL)
attributeID->Release();
if(attributes!=NULL)
attributes->Release();
if(currentNode != NULL)
currentNode->Release();
}

int _tmain(int argc, _TCHAR* argv[])
{

IXMLDOMDocumentPtr xmlFile = NULL;
IXMLDOMElement* xmlRoot = NULL;
_variant_t varXml(L"C:\\demo1.xml");

logFile = CreateFile(L"log.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if(logFile == INVALID_HANDLE_VALUE)
goto fail;

TESTHR(CoInitialize(NULL));

TESTHR(xmlFile.CreateInstance("Msxml2.DOMDocument.4.0"));

VARIANT_BOOL varOut;
TESTHR(xmlFile->load(varXml, &varOut));

TESTHR(xmlFile->get_documentElement(&xmlRoot));

BSTR rootName;
DWORD dwBytesWritten;
TESTHR(xmlRoot->get_nodeName(&rootName));
WriteFile(logFile, rootName, (DWORD)(wcslen(rootName)*sizeof(WCHAR)), &dwBytesWritten, NULL);
WriteFile(logFile, L"\r\n", (DWORD)(2*sizeof(WCHAR)), &dwBytesWritten, NULL);

IXMLDOMNodeList* xmlChildNodes = NULL;
TESTHR(xmlRoot->get_childNodes(&xmlChildNodes));

PrintChild(xmlChildNodes, 2);

fail:
if(logFile != INVALID_HANDLE_VALUE)
CloseHandle(logFile);
if(xmlChildNodes!=NULL)
xmlChildNodes->Release();
if(xmlRoot!=NULL)
xmlRoot->Release();
return 0;
}
taodm 2006-11-20
  • 打赏
  • 举报
回复
直接用现成的xml库不就可以了
Bible_Chou 2006-11-20
  • 打赏
  • 举报
回复
再顶。
大家提下比较具体的意见阿。
加载更多回复(1)

65,186

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧