一个C++解析HTML的库

HTTP协议使用广泛,相应的,C++在这块需求也开始增加。一个好的解析库可以达到事半功倍的效果,在此贴出我的解析库的代码,方便新手朋友们使用。

hHttpParse.h

#ifndef __H_HTML_PARSE_H__
#define __H_HTML_PARSE_H__

#pragma once

#include <Windows.h>
#include <WinInet.h>
#include <string>
#include <cstdio>

class hHtmlParse {
	std::string data;
	int p;
public:
	//构造函数,传入HTML代码
	hHtmlParse (std::string& data);
	//获取网页的编码方式
	bool GetCharset (std::string& s);
	//设置当前解析位置
	bool SetPos (const char* find);
	//设置当前解析位置(反向查找目标位置)
	bool SetPos_LastOf (const char* find);
	//查找是否存在目标位置,不会更新当前位置
	bool find (const char* find);
	//匹配一串字符串,使用sscanf_s获取
	bool MatchString (const char* match, std::string& s);
	//获取当前电脑IP地址
	static bool GetLocalIp (std::string& ip);
	//查询某地址或某域名信息
	static bool GetAddrMessage (const wchar_t* addr, std::string& data);
	//关键函数,获取lpURL指向的地址的HTML代码,并存入data中
	static bool UrlGetHtml (LPCWSTR lpURL, std::string& data);
};

#endif //__H_HTML_PARSE_H__


hHttpParse.cpp

#include "hHtmlParse.h"

#pragma comment(lib, "WinInet.lib")

hHtmlParse::hHtmlParse (std::string& data) {
	this->data = data;
	this->p = 0;
}

bool hHtmlParse::GetCharset (std::string& s) {
	this->SetPos ("charset=");
	return this->MatchString ("%*[\"]%[^\"]", s);
}

bool hHtmlParse::SetPos (const char* find) {
	int t = this->data.find (find, p);
	if (-1 == t) return false;
	this->p = t + strlen (find);
	return true;
}

bool hHtmlParse::SetPos_LastOf (const char* find) {
	int t = this->data.rfind (find);
	if (-1 == t) return false;
	this->p = t + strlen (find);
	return true;
}

bool hHtmlParse::find (const char* find) {
	int t = this->data.find (find, p);
	return t != -1;
}

bool hHtmlParse::MatchString (const char* match, std::string& s) {
	return sscanf_s (&data.c_str () [p], match, const_cast<char*>(s.c_str ()), s.capacity ()) > 0;
}

bool hHtmlParse::GetLocalIp (std::string& ip) {
	std::string page, match;
	page.resize (512);
	match.resize(16);
	if (!hHtmlParse::UrlGetHtml (L"http://1111.ip138.com/ic.asp", page)) return false;
	hHtmlParse hp (page);
	hp.SetPos ("<center>");
	hp.MatchString ("%*[^0-9]%[0-9.]", match);
	ip.clear ();
	ip = match.c_str ();
	return true;
}

bool hHtmlParse::GetAddrMessage (const wchar_t* addr, std::string& data) {
	data.clear ();
	std::wstring link = L"http://www.ip138.com/ips138.asp?ip=";
	std::string page, match;
	link += addr;
	page.resize (16384);
	match.resize (64);
	if (!hHtmlParse::UrlGetHtml (link.c_str (), page)) return false;
	hHtmlParse hp (page);
	hp.SetPos_LastOf ("<table");
	hp.SetPos ("<td");
	hp.SetPos ("<td");
	bool b = true;
	if (hp.find (">>")) {
		b = false;
		hp.SetPos (">>");
		hp.MatchString ("%*[^0-9]%[0-9.]", match);
		data = match.c_str ();
	}
	hp.SetPos ("<ul");
	while (hp.find ("<li")) {
		hp.SetPos (":");
		hp.MatchString ("%[^<]", match);
		if (b) b = false; else data += "\n";
		data += match.c_str ();
	}
	return true;
}

bool hHtmlParse::UrlGetHtml (LPCWSTR lpURL, std::string& data) {
	HINTERNET hSession = InternetOpenW (L"EmotionSniffer", NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE);
	if (!hSession) return FALSE;
	HINTERNET hFile = InternetOpenUrlW (hSession, lpURL, NULL, NULL, INTERNET_FLAG_RELOAD, NULL);
	if (!hFile) {
		InternetCloseHandle (hSession); return FALSE;
	}
	DWORD dwW = 0, dwR = 0;
	int capacity = data.capacity ();
	do {
		dwW += dwR;
		if (dwW != 0 && dwR == 0) break;
		if (dwW + 1024 >= capacity) data.resize (capacity *= 2);
	} while (InternetReadFile (hFile, (LPVOID) (data.c_str () + dwW), 1024, &dwR));
	const_cast<char*>(data.c_str ()) [dwW] = '\0';
	InternetCloseHandle (hFile);
	InternetCloseHandle (hSession);
	return TRUE;
}

简要说明下使用方法。首先是封装的三个静态函数, GetLocalIp 和 GetAddrMessage 这俩是通过调用 www.ip138.com 动态查询获取的结果,调用这个库实现。使用这个库时可以参照上面两个函数的代码; UrlGetHtml 是通过 Windows 的 Internet API 实现从 URL 指定的地址下载网页。

重点说说这个库的使用方法,我就说说 hHtmlParse::GetLocalIp 则函数的实现,方法大多类似。

1、字符串定义

std::string page, match;
page.resize (512);
match.resize (16);

其中 page 用作保存 HTML 代码, match 用作保存匹配的字符串,也就是网页中需要获取的数据。由于访问的数据不大,所以 page 设置 512 字节足够。这儿也可以不用设置大小的, UrlGetHtml 实现的比较智能,可以自动扩展大小。设置一个大小只是可以减少内存 I/O ,提高执行速度。另外这儿也给 match 设置一个大小。对于IP地址来说,16字节足够了。

2、获取网页HTML代码

if (!hHtmlParse::UrlGetHtml (L"http://1111.ip138.com/ic.asp", page)) return false;
这句话的意思就是下载网页并将网页代码保存在 page 中,如果执行失败则返回。

3、创建解析对象

hHtmlParse hp (page);
这句代码用于创建一个解析对象,传入的数据为网页HTML字符串。
这里我们看看ip138的网页代码:

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=gb2312">
<title> 您的IP地址 </title>
</head>
<body style="margin:0px"><center>您的IP是:[123.144.*.*] 来自:XX市 联通</center></body></html>
地址和位置打码了,大家看得懂就行了。我们先来分析分析,需要获取地址的代码前面有一个 <center> 是吧?那就把位置设置在这儿吧。。。
4、设置当前解析位置

hp.SetPos ("<center>");
在这个解析库内部维护着一个字符指针,假如说网页前面的都解析过了,需要解析后面的,那就在解析时给字符指针赋值,然后下次解析时从字符指针位置开始解析,既方便网页处理,也提高解析速度,一举两得。这行代码的意思就是将内部维护的地址放在 <center> 这儿,下次调用时直接从这儿开始了。

5、获取匹配字符串,也就是IP地址

hp.MatchString ("%*[^0-9]%[0-9.]", match);
这儿就是获取匹配字符串的代码了 ,第一个参数为sscanf函数需要的那个字符串,match返回匹配的结果。简要说说这个字符串的意思,需要深究的自行bing。

%*[^0-9]   %*表示跳过,不匹配,[]表示匹配的内容,^表示非,0-9表示那十个数字。连起来的意思就是跳过所有不是0-9的字符。

%[0-9.]   %表示匹配,0-9.表示匹配的内容为那十个数字和小数点,一直到非0-9或小数点为止。

代码到这儿就已经获取了匹配的ip了,接下来的内容就是简单的处理了。

6、简单处理并返回

ip.clear ();
ip = match.c_str ();
return true;

由于匹配是通过 const_cast<char*>(s.c_str()) 赋值(这行代码可以在 hHtmlParse::MatchString 函数中找到),所以,在执行步骤6这段代码前,调用 match.length() 实际上返回的是不固定值,虽然不固定但是有数据。所以,假如非得在 const_cast<char*>(s.c_str()) 之后获取字符串长度,只能用lstrlen。

这几行代码大家应该都懂,简要说说第二行,意思是调用 std::string 的重载函数 operator=(char*) ,这样可以刷新 std::string 的长度,执行第二行代码之后,ip中的数据可以直接调用 length() 来获取长度了。

使用步骤描述完毕,同学们如果需要解析其他网页在相应地方修改就行了。另外, SetPos 和 MatchString 并不是只能调用一次的,只要找网页方便,并且保证对象内部维护的指针还没到末尾,就可以重复调用这些函数了。

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 好的。以下是用 C 语言编写的简单 Markdown 解析器的示例代码: ```c #include <stdio.h> #include <string.h> #define BUF_SIZE 1024 int main(int argc, char *argv[]) { char buf[BUF_SIZE]; while (fgets(buf, BUF_SIZE, stdin) != NULL) { int i = 0; while (buf[i] != '\0') { // 处理标题 if (buf[i] == '#') { // 输出 "<h1>" printf("<h1>"); i++; // 跳过 # 号后面的空格 while (buf[i] == ' ') i++; // 输出标题内容,直到遇到换行符 while (buf[i] != '\n' && buf[i] != '\0') putchar(buf[i++]); // 输出 "</h1>" printf("</h1>"); } // 处理加粗文本 else if (buf[i] == '*' && buf[i + 1] == '*') { // 输出 "<strong>" printf("<strong>"); i += 2; // 输出加粗文本内容,直到遇到两个连续的星号 while (buf[i] != '\0' && !(buf[i] == '*' && buf[i + 1] == '*')) putchar(buf[i++]); // 输出 "</strong>" printf("</strong>"); i += 2; } // 处理普通文本 else { putchar(buf[i++]); } } } return 0; } ``` 这个程序读入 Markdown 格式的文本,并输出对应的 HTML 格式的文本。它只处理了标题和加粗文本两种格式,其他的格式会被当作普通文本输出。 在这个程序中,我们使用了标准 C 函数 `fgets` 来读入输入流中的一 ### 回答2: Markdown是一种轻量级标记语言,常用于撰写文档、编写博客或编辑readme文件。编写一个Markdown解析器的目的是将Markdown文本转换为HTML或其他格式的文档。可以使用C语言来编写一个简单的Markdown解析器。以下是一个例子: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct { char* input; char* output; } Markdown; void parseMarkdown(Markdown* md) { // 解析处理Markdown文本的代码 // ... // 将解析后的结果赋值给md->output } int main() { Markdown md; md.input = "这是*斜体*文字。"; parseMarkdown(&md); printf("%s\n", md.output); free(md.output); return 0; } ``` 在这个例子中,我们首先定义了一个Markdown结构体,包含`input`和`output`两个成员。`input`成员保存了待解析的Markdown文本,`output`成员保存解析后的结果。在`parseMarkdown`函数中可以编写相应的代码来处理Markdown文本,并将结果赋值给`md->output`。 在`main`函数中,我们创建了一个Markdown结构体md,并将待解析的Markdown文本传递给`parseMarkdown`函数进行解析。之后,我们可以输出解析后的结果。 当然,这个例子只是一个简化的版本,实际的Markdown解析器可能更加复杂。解析器需要处理各种Markdown的语法,例如标题、列表、表格等,并将其转换为相应的HTML或其他格式的文档。编写一个完整功能的Markdown解析器需要更多的代码和技术,但这个例子可以帮助你入门。 ### 回答3: Markdown是一种轻量级标记语言,通常用于写作文档和网页。C语言是一种高效而广泛使用的编程语言,所以使用C编写一个Markdown解析器是可行的。 Markdown解析器可以将Markdown语法转换为HTML或者其他格式。解析器的功能包括识别Markdown语法中的特殊符号和格式,并将其转换为相应的HTML标签或其他格式的文本。 编写Markdown解析器可以分为几个步骤: 1. 读取Markdown文件或文本; 2. 解析文本,识别Markdown语法中的特殊符号和格式; 3. 根据识别的语法,生成相应的HTML标签或其他格式的文本; 4. 输出转换后的HTML标签或其他格式的文本。 在C语言中,可以通过读取文件的方式获取Markdown文本,并使用字符串处理函数来解析和转换文本。可以使用条件语句和循环语句来检查和处理不同的Markdown语法。 对于较复杂的Markdown语法,可能需要使用正则表达式来进行匹配和替换。C语言中可以使用正则表达式来处理正则表达式操作。 需要注意的是,在编写Markdown解析器时,需要仔细处理特殊字符、转移字符和嵌套标签等情况,以确保解析结果的正确性和一致性。 总之,通过使用C语言的字符串处理函数、条件语句、循环语句和正则表达式,可以编写一个Markdown解析器,将Markdown语法转换为HTML或其他格式的文本。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值