正则表达式1.1(Parser的简单实现)

今天实现了一个递归向下的RegexParser, 可以把正则表达式转化为语法树.

Russ Cox正则表达式解析里面here, 把正则式子首先转化成了逆波兰表达式.

语法树的表达能力要比逆波兰要好一些, 而且树的的后序遍历就是逆波兰式.

大概就是对于每一种运算符, 实施一个parse的子过程, 我之所以考虑使用递归实现, 是考虑到这样写以后扩展的话码代码会方便一些XDD.

下面给出Parser的代码:

  1 //Symbol.h
  2 #ifndef __SYMBOL_H__
  3 #define __SYMBOL_H__
  4 
  5 
  6 enum TokenType
  7 {
  8     EOF = -1,
  9     START = 0,//^
 10     PLUS, //+
 11     STAR,//*
 12     BRACKET, //()
 13     SQUARE, //[]
 14     QUESTION, // ?
 15     END, //$
 16     CONCAT, //.
 17     ALT, //|
 18     DOT, //
 19     ALPHA, //
 20     NUM,
 21 };
 22 
 23 enum DumpOrder
 24 {
 25     PRE, MID, POST
 26 };
 27 
 28 
 29 #endif // __SYMBOL_H__
 30 
 31 
 32 //Parser.h
 33 #ifndef __PARSER_H__
 34 #define __PARSER_H__
 35 
 36 #include <cstdlib>
 37 #include <ctype.h>
 38 #include <cstring>
 39 #include <stack>
 40 #include "Symbol.h"
 41 
 42 using namespace std;
 43 
 44 #define MAXN 1000000
 45 
 46 typedef struct Node
 47 {
 48     TokenType token;
 49     int subType; //<By张方雪 2013-5-18>用于处理一种token会对应多种的情形.
 50     Node* left;
 51     Node* right;
 52 
 53     void Init(TokenType tok, int subT, Node* l, Node* r)
 54     {
 55         token = tok;
 56         subType = subT;
 57         left = l;
 58         right = r;
 59     }
 60 }Node;
 61 
 62 
 63 
 64 class Parser
 65 {
 66 public:
 67     Parser();
 68     ~Parser();
 69     Node* Parse(char* regexStr);
 70     void Dump(Node* root, DumpOrder order);
 71 
 72 private:
 73     Node* _buffer;
 74     int _used;
 75     int _pos;
 76     char* _regex;
 77     int _len;
 78 
 79     Node* NewNode()
 80     {
 81         return &_buffer[_used++];
 82     }
 83     char CurrentToken()
 84     {
 85         if(_pos >= _len) return -1;
 86         return _regex[_pos];
 87     }
 88     void NextToken()
 89     {
 90         _pos++;
 91     }
 92 
 93     Node* Parse();// recursive implementation
 94     Node* ParseAlt();
 95     Node* ParseConcat();
 96     Node* ParsePlus();
 97     Node* ParseBracket();
 98     Node* ParseDot();
 99     Node* ParseAtom();
100     Node* ParseMulti();
101 
102     void DumpMidOrder(Node* root);
103     void DumpPostOrder(Node* root);
104 
105 };
106 
107 
108 #endif // __PARSER_H__
109 
110 
111 
112 //Parser.cpp
113 #include <iostream>
114 #include "Parser.h"
115 
116 
117 using namespace std;
118 
119 
120 Parser::Parser()
121 {
122     _used = 0;
123     _buffer = new Node[MAXN];
124     _pos = 0;
125     _regex = new char[MAXN];
126 
127 }
128 Parser::~Parser()
129 {
130     delete []_buffer;
131 }
132 
133 
134 Node* Parser::Parse(char* regexStr)
135 {
136     strcpy(_regex, regexStr);
137     _len = strlen(regexStr);
138     _pos = 0;
139     _used = 0;
140 
141     return Parse();
142 }
143 
144 Node* Parser::Parse()
145 {
146     return ParseAlt();
147 }
148 
149 Node* Parser::ParseAlt()
150 {
151     Node* root = ParseConcat();
152     char token = CurrentToken();
153 
154     if(token == '|')
155     {
156         NextToken();
157         Node* leftChild = root;
158         Node* rightChild = ParseAlt();
159         root = NewNode();
160         root->Init(ALT, 0, leftChild, rightChild);
161     }
162 
163     return root;
164 
165 }
166 
167 Node* Parser::ParseConcat()
168 {
169     Node* root = ParseMulti();
170     char token = CurrentToken();
171 
172     if(isalnum(token) || token == '(')
173     {
174         Node* leftChild = root;
175         Node* rightChild = ParseConcat();
176         root = NewNode();
177 
178         root->Init(CONCAT, 0, leftChild, rightChild);
179 //        Dump(root, MID);
180     }
181 
182     return root;
183 }
184 
185 Node* Parser::ParseMulti()
186 {
187     Node* root = ParseBracket();
188     char token = CurrentToken();
189     while(token == '+' || token == '*' || token == '?')
190     {
191         Node* leftChild = root;
192         root = NewNode();
193         switch(token)
194         {
195         case '+':
196             root->Init(PLUS, 0, leftChild, NULL);
197             break;
198         case '?':
199             root->Init(QUESTION, 0, leftChild, NULL);
200             break;
201         case '*':
202             root->Init(STAR, 0, leftChild, NULL);
203             break;
204         }
205         NextToken();
206         token = CurrentToken();
207     }
208     return root;
209 }
210 
211 Node* Parser::ParseBracket()
212 {
213     Node* root = NULL;
214     char token = CurrentToken();
215     if(token == '(')
216     {
217         NextToken();
218         root = ParseAlt();
219         if(CurrentToken() != ')')
220         {
221             cout << "Error " <<  CurrentToken() << endl;
222             return NULL;
223 
224         }
225         NextToken();
226     }
227     else
228     {
229         root = ParseAtom();
230     }
231     return root;
232 }
233 
234 Node* Parser::ParseAtom()
235 {
236     Node* root = NULL;
237     char token = CurrentToken();
238 
239     if(isalpha(token))
240     {
241         root = NewNode();
242         root->Init(ALPHA, token, NULL, NULL);
243         NextToken();
244         return root;
245     }
246     else if(isalnum(token))
247     {
248         root = NewNode();
249         root->Init(NUM, token, NULL, NULL);
250         NextToken();
251         return root;
252     }
253     else if(token == '.')
254     {
255         root = NewNode();
256         root->Init(DOT, 0, NULL, NULL);
257         NextToken();
258         return root;
259     }
260     else
261     {
262         cout << "出现未能处理的token" << endl;
263         return NULL;
264     }
265 
266 
267 }
268 
269 void Parser::DumpMidOrder(Node* root)
270 {
271     if(root == NULL) return;
272     switch(root->token)
273     {
274     case ALPHA:
275     case NUM:
276     {
277         cout << char(root->subType) ;
278         break;
279     }
280     case PLUS:
281     {
282         cout << "(";
283         DumpMidOrder(root->left);
284         cout << ")+";
285         break;
286     }
287     case STAR:
288     {
289         cout << "(";
290         DumpMidOrder(root->left);
291         cout << ")*";
292         break;
293     }
294     case QUESTION:
295     {
296         cout << "(";
297         DumpMidOrder(root->left);
298         cout << ")?";
299         break;
300     }
301     case CONCAT:
302     {
303         DumpMidOrder(root->left);
304         DumpMidOrder(root->right);
305         break;
306     }
307     case DOT:
308     {
309         cout << ".";
310         break;
311     }
312     case ALT:
313     {
314         DumpMidOrder(root->left);
315         cout << "|";
316         DumpMidOrder(root->right);
317         break;
318     }
319     default:
320     {
321         cout << "非法的符号: " << root->token <<  endl;
322         return;
323     }
324     }
325 }
326 
327 void Parser::DumpPostOrder(Node* root)
328 {
329     if(root == NULL) return;
330     switch(root->token)
331     {
332     case ALPHA:
333     case NUM:
334     {
335         cout << char(root->subType) ;
336         break;
337     }
338     case PLUS:
339     {
340         DumpPostOrder(root->left);
341         cout << "+";
342         break;
343     }
344     case STAR:
345     {
346         DumpPostOrder(root->left);
347         cout << "*";
348         break;
349     }
350     case QUESTION:
351     {
352         DumpPostOrder(root->left);
353         cout << "?";
354         break;
355     }
356     case CONCAT:
357     {
358         DumpPostOrder(root->left);
359         DumpPostOrder(root->right);
360         cout << "$"; //<By张方雪 2013-5-18>先用这个当连接符吧.
361         break;
362     }
363     case DOT:
364     {
365         cout << ".";
366         break;
367     }
368     case ALT:
369     {
370         DumpPostOrder(root->left);
371         DumpPostOrder(root->right);
372         cout << "|";
373         break;
374     }
375     default:
376     {
377         cout << "非法的符号: " << root->token <<  endl;
378         return;
379     }
380     }
381 }
382 
383 void Parser::Dump(Node* root, DumpOrder order)
384 {
385     if(order == MID)
386     {
387         DumpMidOrder(root);
388     }
389     else if(order == POST)
390     {
391         DumpPostOrder(root);
392     }
393 
394     cout << endl;
395 }
396 
397 
398 
399 
400 
401 //test.cpp
402 #include <iostream>
403 #include "Parser.h"
404 
405 using namespace std;
406 
407 int main()
408 {
409     cout << "Hello world!" << endl;
410     Parser parser;
411     Node* root = parser.Parse("p|a(c*|.f++)+(cd|F|W|C)**");
412     parser.Dump(root, MID);
413     root = parser.Parse("a|b|c|d+c*");
414     parser.Dump(root, POST);
415     return 0;
416 }

 代码写得乱糟糟, 恐有bug XD.

posted on 2013-05-18 21:34  Jackiesteed 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/jackiesteed/articles/3086205.html

<think>好的,我现在需要帮用户解决如何使用Python的requests模块和正则表达式或BeautifulSoup来抓取西安天气数据,并进行数据分析和可视化的问题。用户之前问过关于VSCode配置LaTeX的问题,但当前的问题是关于网页爬虫和数据分析的,所以需要专注于当前需求。 首先,我需要确认用户的引用内容是否相关。用户提供的引用是关于Scrapy项目文件的,但用户现在想用requests和BeautifulSoup,所以可能引用不太相关,但需要检查是否有隐藏需求。不过用户明确说引用仅供参考,所以可以忽略,专注于当前问题。 接下来,用户的需求是抓取http://www.tianqihoubao.com/weather/top/xian.html中的天气数据,提取一个月内每日的天气状况、最高温度和最低温度,然后进行温度变化曲线和天气状况统计的可视化。 我需要分解步骤: 1. 分析目标网页结构,确认数据的位置。可能需要查看网页的HTML结构,找到包含天气数据的表格或列表。 2. 使用requests发送HTTP请求获取网页内容,处理可能的编码问题。 3. 使用BeautifulSoup或正则表达式解析HTML,提取所需数据。可能需要找到对应的HTML标签、类名或ID。 4. 数据清洗,将提取的文本转换为合适的格式,比如日期、温度值等。 5. 存储数据,可能用列表或Pandas DataFrame。 6. 使用Matplotlib或Seaborn进行可视化,绘制温度变化曲线图和天气状况的统计图(如柱状图或饼图)。 7. 确保代码的健壮性,处理异常,比如网络请求失败或网页结构变化。 可能遇到的问题: - 网页是否有反爬机制,是否需要设置headers模拟浏览器访问。 - 数据是否存在动态加载,是否需要处理JavaScript渲染的内容。但用户提供的网址看起来是静态页面,所以可能不需要Selenium。 - 日期格式的处理,温度数据的提取(可能需要去除无关字符,如℃)。 - 可视化时的图表美观和可读性。 需要验证网站是否可访问,检查robots.txt是否有爬虫限制。但用户提供的网站可能需要实际访问确认结构。 接下来,编写代码的大致步骤: - 发送请求,获取HTML。 - 解析HTML,提取日期、天气状况、温度。 - 转换温度数据为数值类型。 - 使用Pandas存储数据。 - 使用Matplotlib绘图。 可能需要使用到re模块的正则表达式来提取温度,或者直接用BeautifulSoup的find_all方法。 另外,用户提到使用正则表达式或其他解析工具,所以可以提供两种方法,但推荐BeautifulSoup,因为更稳定易读。 在数据可视化部分,需要绘制折线图和饼图,展示温度变化和天气状况分布。 最后,可能需要提醒用户注意网站的使用条款,合法爬取数据,以及设置适当的请求间隔,避免给服务器造成压力。</think>### 西安天气数据抓取与分析完整方案 #### 一、网页结构分析(以2024年1月数据为例) 目标网页包含历史天气数据表格,数据存储在`<table>`标签中。通过开发者工具可确认数据表格结构: ```html <table class="b" cellpadding="0" cellspacing="0"> <tr> <td>日期</td> <td>天气状况</td> <td>气温</td> <td>风力风向</td> </tr> <tr> <td>2024-01-01</td> <td>晴转多云</td> <td>4℃/-3℃</td> <td>北风3-4级</td> </tr> <!-- 更多数据行 --> </table> ``` #### 二、数据抓取实现 ```python import requests from bs4 import BeautifulSoup import pandas as pd import re # 配置请求头防止反爬 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' } def fetch_weather_data(): url = 'http://www.tianqihoubao.com/weather/top/xian.html' response = requests.get(url, headers=headers) response.encoding = 'gbk' # 该网站使用GBK编码 soup = BeautifulSoup(response.text, 'html.parser') table = soup.find('table', class_='b') weather_data = [] for row in table.find_all('tr')[1:]: # 跳过表头 cols = row.find_all('td') # 正则提取温度数值 temp_text = cols[2].get_text().strip() temps = re.findall(r'(-?\d+)℃', temp_text) weather_data.append({ '日期': cols[0].get_text().strip(), '天气': cols[1].get_text().strip(), '最高温': int(temps[0]), '最低温': int(temps[1]), '风力': cols[3].get_text().strip() }) return pd.DataFrame(weather_data) # 执行数据抓取 df = fetch_weather_data() print(df.head()) ``` #### 三、数据分析与可视化 ```python import matplotlib.pyplot as plt import seaborn as sns # 配置可视化样式 plt.rcParams['font.sans-serif'] = ['SimHei'] # 解决中文显示问题 sns.set_style("whitegrid") # 温度变化曲线 plt.figure(figsize=(12, 6)) plt.plot(df['日期'], df['最高温'], marker='o', label='最高气温') plt.plot(df['日期'], df['最低温'], marker='s', label='最低气温') plt.title('西安月气温变化趋势') plt.xlabel('日期') plt.ylabel('温度()') plt.xticks(rotation=45) plt.legend() plt.tight_layout() plt.show() # 天气状况统计 weather_stats = df['天气'].value_counts() plt.figure(figsize=(8,8)) weather_stats.plot.pie(autopct='%1.1f%%', startangle=90) plt.title('天气状况分布') plt.ylabel('') plt.show() ``` #### 四、关键技术点说明 1. **编码处理**:该网站使用GBK编码,需显式设置`response.encoding`[^1] 2. **数据清洗**:使用正则表达式`r'(-?\d+)℃'`精确提取温度数值 3. **可视化优化**: - 日期标签旋转45度避免重叠 - 使用Seaborn增强图表可读性 - 饼图设置`startangle`参数优化显示角度 #### 五、扩展功能建议 1. **数据存储**:可添加`df.to_csv('xian_weather.csv')`保存原始数据 2. **异常处理**:建议添加重试机制和代理设置 3. **动态更新**:使用`schedule`库实现每日自动抓取 ```python # 增强版数据存储示例 df.to_csv('xian_weather.csv', index=False, encoding='utf_8_sig') # 兼容Excel中文显示 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值