Regular Expression实现

2023-11-09

主要分2大块,

核心部分:就是一个NFA,只支持标准正则的操作,concatenation, union, iteration,限定上限的iteration,对应的meta character只有 (, ), |, *, ., {upper}

扩展部分:这部分是把扩展正则表达式转化为标准正则表达式,扩展正则表达式支持集合[a-z0-9],  重复次数上下界限制 {lower, upper},+, ?等

一:核心NFA部分

对于DFA来说,内部就是一张状态转移表table[state][next input char];对于NFA,内部就是一个epsilon状态转移有向图。DFA的状态转移必须由输入字符触发,而且转移是确定的。NFA可以不读输入进行转移(而且有多种转移),对应meta character,比如*,可以back reference之前的字符,而“(”和“)”也不需要consume输入,因为是meta character,不对应输入字符。NFA的不确定体现在,有多种转移,可以到达多种状态,那些状态又可以有多种转移,所以是一个Digraph的dfs问题,所以判断match的条件是从初始状态出发dfs能够到达终止状态,是一个路径搜索问题。

1 simulate(判断匹配)

1)从状态0 dfs,遍历epsilon转移,得出一个初始状态集

2)对于当前输入字符,

2).1 尝试从当前状态集的每个状态匹配,这个是consume输入的。转移到下一个状态,if (re.charAt(v) == c || re.charAt(v) == '.') states.add(v + 1)

2).2 从2).1得到的新的状态集出发多源dfs, 进行epsilon转移遍历,得出新的状态集,作为下一个字符对应的状态集

3)match判断,最终状态集里如果包含终止状态,说明终止状态是可达的。


2 epsilon转移Digraph的构建,读入标准正则表达式

1)对于(,)和* 添加一条到下一个字符的转移

2)对于iteration操作符*,添加*到back reference的实体的起始位置的双向转移,实体是单个字符或者子regex(被小括号括着)

3)对于Union操作符|,规定必须由小括号括着,即必须是(a|b),添加从(到|之后的实体的转移,以及|之后的实体到)的转移

4)对于有上限的iteration {upper}, 和*类似,添加从{到之前实体的双向转移,和从{到下一个字符(}之后)的转移。但是从{往前back reference的转移的边是带权的,权值就是重复次数 - 1。为了支持限定次数的iteration,Digraph是带权的,普通边权值设定为-1,带整数权值的边表示这条边只能走这么多次,相应的dfs算法也会做判断,每走一次decrease一次权值,直到0,0表示这条边不能走,相当于没有。


二:扩展的正则表达式支持

对原始输入的正则表达式进行处理,转化成标准正则表达式

[a-z0-9] 转化成((a|b)|c...

x+ 转化成 xx*

x?转化成x{1}

x{2, 3}转化成 xx{3-2}即xx{1}

[^abc] 先求差集charset - [abc],在转化成((a|b)|c)的形式

package excercise;
import java.util.*;
class Edge {
	int from, to, weight;
	public Edge(int from, int to, int w) {
		this.from = from;
		this.to = to;
		this.weight = w;
	}
}
class Digraph {
	private List<Edge>[] adj;
	private int V;
	public Digraph(int V) {
		this.V = V;
		adj = new List[V];
		for (int v = 0; v < V; ++v)
			adj[v] = new ArrayList<Edge>();
	}
	public void addEdge(int v, int w, int weight) {
		this.adj[v].add(new Edge(v, w, weight));
	}
	public void addEdge(int v, int w) { addEdge(v, w, -1);}
	public int V() {return V;}
	public Iterable<Edge> adj(int v) { return adj[v];}
}
class DirectedDFS {
	private boolean[] marked;
	private Digraph G;
	public DirectedDFS(Digraph G, int v) {
		marked = new boolean[G.V()];
		this.G = G;
		dfs(v);
	}
	public DirectedDFS(Digraph G, Iterable<Integer> s) {
		marked = new boolean[G.V()];
		this.G = G;
		for (int v : s) dfs(v);
	}
	public boolean marked(int v) {return marked[v];}
	private void dfs(int v) {
		marked[v] = true;
		for (Edge e : G.adj(v)) {
			if (!marked[e.to]) {
				if (e.weight < 0) dfs(e.to);
				else if (e.weight > 0) {
					e.weight--;
					dfs(e.to);
				}
			}
		}
	}
}
class NFA {
	private String re;
	private Digraph G;
	private int M;
	public NFA(String regex) {
		re = regex;
		M = regex.length();
		G = buildEpsilonTransitionGraph();
	}
	private Digraph buildEpsilonTransitionGraph() {
		Digraph G = new Digraph(M + 1);		
		Stack<Integer> ops = new Stack<Integer>();
		for (int i = 0; i < M; ++i) {
			int lp = i;
			if (re.charAt(i) == '(' || re.charAt(i) == '|') ops.push(i);
			else if (re.charAt(i) == ')') {
				int or = ops.pop();
				if (re.charAt(or) == '|') {
					lp = ops.pop();
					G.addEdge(or, i);
					G.addEdge(lp, or + 1);
				}
				else lp = or;
			}
			if (i < M - 1 && re.charAt(i + 1) == '*') {
				G.addEdge(lp, i + 1);
				G.addEdge(i + 1, lp);
			}
			if (i < M - 1 && re.charAt(i + 1) == '{') {
				int rb = re.indexOf('}', i + 1);
				int num = Integer.parseInt(re.substring(i + 2, rb));
				G.addEdge(lp, i + 1);
				G.addEdge(i + 1, lp, num - 1);
			}
			if (re.charAt(i) == '(' || re.charAt(i) == ')' || re.charAt(i) == '*' )
				G.addEdge(i, i + 1);
			else if (re.charAt(i) == '{') {
				int rb = re.indexOf('}', i);
				G.addEdge(i, rb + 1);
			}
		}
		return G;
	}
	public boolean recognizes(String text) {
		List<Integer> e_closure = new ArrayList<Integer>(); 
		DirectedDFS dfs = new DirectedDFS(G, 0);
		for (int v = 0; v < G.V(); ++v) //compute e-closure(0), epsilon closure of start state
			if (dfs.marked(v)) e_closure.add(v);
		
		for (int i = 0; i < text.length(); ++i) {
			char c = text.charAt(i);
			List<Integer> states = new ArrayList<Integer>();
			for (int v : e_closure) { //compute Y = c(X), the state set Y that state set X could goto on c
				if (v == M) continue;
				if (re.charAt(v) == c || re.charAt(v) == '.') states.add(v + 1);
			}
			if (states.isEmpty()) return false;
			dfs = new DirectedDFS(G, states); 
			e_closure = new ArrayList<Integer>(); //compute e-closure(Y)
			for (int v = 0; v < G.V(); ++v)
				if (dfs.marked(v)) e_closure.add(v);
			if (e_closure.isEmpty()) return false;
		}
		
		for (int v : e_closure)
			if (v == M) return true;
		return false;
	}
}
public class RE {
	private NFA nfa;
	private final Set<Character> metaChars;
	
	public RE(String regex) {
		char[] metaArray = {'*', '(', ')', '[', ']', '{', '}', '.', '|'};
		metaChars = new HashSet<Character>();
		for (char c : metaArray) metaChars.add(c);
		String afterProcess = preProcess(regex);
	//	System.out.println(afterProcess);
		nfa = new NFA(afterProcess);
	}
	public  boolean recognizes(String text) {
		return nfa.recognizes(text);
	}
	private String preProcess(String s) {
		StringBuilder sb = new StringBuilder();
		int lastREStart = -1;
		int leftParenthese = -1;
		int or = -1;
		for (int i = 0; i < s.length(); ++i) {
			if (s.charAt(i) == '[') {
				int rb = s.indexOf(']', i + 1);
				lastREStart = sb.length();
				sb.append(handleBracket(s, i, rb));
				i = rb;
			}
			else if (s.charAt(i) == '{') {
				int end = sb.length();
				int comma = s.indexOf(',', i + 1);
				int num = Integer.parseInt(s.substring(i + 1, comma));
				for (int j = 0; j < num; ++j)
					sb.append(sb.substring(lastREStart, end));
				int rb = s.indexOf('}', comma + 1);
				int num2 = Integer.parseInt(s.substring(comma + 1, rb));
				sb.append("{" +  (num2 - num) + "}");
				i = rb;
			}
			else if (s.charAt(i) == '+') {
				sb.append(sb.substring(lastREStart, sb.length()));
				sb.append('*');
			}
			else if (s.charAt(i) == '?') {
				sb.append("{1}");
			}
			else if (s.charAt(i) == '|') {
				if (or > 0) {
					sb.insert(0, '(');
					sb.append(')');
					lastREStart = 0;
				}
				sb.append(s.charAt(i));
				or = sb.length() - 1;
			}
			else {
				if (leftParenthese == -1) lastREStart = sb.length();
				sb.append(s.charAt(i));
				if (s.charAt(i) == '(') leftParenthese = i;
				else if (s.charAt(i) == ')') {
					lastREStart = leftParenthese;
					leftParenthese = -1;
				}
			}
		}
		if (or > 0) {
			sb.insert(0, '(');
			sb.append(')');
			lastREStart = 0;
		}
		return sb.toString();
	}
	private String handleBracket(String s, int l, int r) {
		boolean filter = true;
		boolean[] include = new boolean[256];
		if (s.charAt(l + 1) == '^') {
			filter = false;
			++l;
		}
		for (int i = l + 1; i < r; ++i) {
			if (s.charAt(i) != '-') 
				include[s.charAt(i)] = true;
			else {
				for (char c = (char)(s.charAt(i - 1) + 1); c < s.charAt(i + 1); ++c) 
					include[c] = true;
			}
		}
		ArrayDeque<Character> ad = new ArrayDeque<Character>();
		for (char c = 0; c < 256; ++c) {
			if (include[c] == filter && !metaChars.contains(c)) {
				if (ad.isEmpty()) ad.addLast(c);
				else {
					ad.addLast('|');
					ad.addLast(c);
					ad.addFirst('(');
					ad.addLast(')');
				}
			}
		}
		char[] a = new char[ad.size()];
		for (int i = 0; i < a.length; ++i)
			a[i] = ad.pollFirst();
		return new String(a);
	}
}




本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Regular Expression实现 的相关文章

随机推荐

  • Learning OpenStack Keystone

    Author 海峰 http weibo com 344736086 http yanheven github io http blog csdn net yanheven1 这周重新学习整理了OpenStack Keystone里面的知识
  • CentOS没有了用什么?Rocky Linux 8.6安装体验

    2020 年 12 月 8 日 CentOS 项目宣布 CentOS 8 将于 2021 年底结束 而 CentOS 7 将在其生命周期结束后停止维护 CentOS 7 9 和 CentOS 8 5 将是最后的2个CentOS 版本 官方解
  • concurrentHashMap解析这篇文章就够了

    实现原理 ConcurrentHashMap使用分段锁技术 将数据分成一段一段的存储 然后给每一段数据配一把锁 当一个线程占用锁访问其中一个段数据的时候 其他段的数据也能被其他线程访问 能够实现真正的并发访问 如下图是ConcurrentH
  • 使用 Python 操作 MongoDB

    使用 Python 操作 MongoDB MongoDB 是一个开源的面向文档的 NoSQL 数据库 它具有高性能 可扩展性和灵活性的特点 通过使用 Python 的 pymongo 模块 我们可以方便地操作 MongoDB 数据库 本文将
  • CPU工作原理和MMU初探

    具体相关内容主要参考自一篇博客 当然有结合其它内容 感谢博主提供的资源 这里附上参考链接 http www cnblogs com xiangtao archive 2013 04 11 3014815 html 关于CPU和MMU需要做几
  • 企业微信第三方应用-应用客服会话(h5)

    企业微信中第三方应用 h5 不能像小程序那样将button标签的open type属性设置为contact即可跳转到客服会话页面 但是js sdk为了开发者提供了openThirdAppServiceChat Api 让用户可快速打开应用客
  • IT项目管理作业五

    一 你联合同学做一个年级微信公众号加强各班相互了解 联合活动 等 请写一份两页的报告 描述收集需求的方法 并附上收集的 需求跟踪矩阵 不少于五个需求 收集需求的方法 数据收集方面 头脑风暴 召集项目所有的参与成员 共同讨论关于微信公众号对于
  • Python Class

    关键字1 self self指代 类的实例化 而不是类本身 class Test def prt self print self print self class t Test t prt result
  • 从事Java三年多,去应聘16k最后没被录用,细节如下……

    前言 今天小编和大家分享一位以前面试的一位应聘者 工作4年26岁 统招本科 以下就是他的简历和面试情况 基本情况 专业技能 1 熟悉Sping了解SpringMVC SpringBoot Mybatis等框架 了解SpringCloud微服
  • 和平精英服务器位置,和平精英音乐盒在哪里 地图详细位置介绍

    和平精英体验服最近重新开放服务器 不少玩家在游戏中发现了新内容 特别是热度特别高的万圣节模式 很多小伙伴会问和平精英音乐盒在哪里 快随小编来看看吧 在所有地图的一些房区里面是会随机刷新出音乐盒的 当我们发现音乐盒后是可以与这个道具互动的 我
  • 2023前端面试题——JS篇

    1 判断 js 类型的方式 1 typeof 可以判断出 string number boolean undefined symbol 但判断 typeof null 时值为 object 判断数组和对象时值均为 object 2 inst
  • 避免陷入信息茧房

    目录 一 什么是信息茧房 二 做什么容易陷入信息茧房 三 如何避免陷入信息茧房 总结 一 什么是信息茧房 信息茧房 Echo Chamber 是指在社交媒体和互联网环境中 个体被限制在一种信息和观点的环境中 与自己持相似观点的人群形成闭环
  • 目标文件格式分析工具: ar,nm,objdump,objcopy,readelf

    http www kgdb info linuxdev object analyse tools 目标文件格式分析工具 ar nm objdump objcopy readelf 2011年9月5日 reship 发表评论 阅读评论 本文转
  • 推荐算法:基于内容的推荐_1:内容推荐算法

    基于内容的推荐 推荐给用户他们过去喜欢的类似产品 基于CF的推荐 识别出具有相同爱好的用户 给他们推产品 基于内容的推荐算法 基于内容推荐的步骤 对数据内容分析 得到物品的结构化描述 分析用户过去的评分或评论过的物品的 作为用户的训练样本
  • nabc模型_WHϵÁÐÔ²»¡Ô²ÖùÎϸ˼õËÙ»ú3DÁ¢ÌåÄ£ÐÍ_¼õËÙ»ú_¼õËÙÆ÷_Öйú¼õËÙ»úÐÅÏ¢Íøwww.jiansuji001.com...

    OzsgSFNGIFYxMy4wNSAKSQAAAABCAFTjJb68dJO9QmDluwAAAD4nMQg TDeJPlp42ux9B0BU17bonjnnTC 0XobeYWYYYCiD2BGNhSYiKogoKqKCBStjixqj
  • vscode打开代码,注释中的中文显示乱码

    问题如下 np random seed 2017 瀹氫箟闅忔満鏁扮殑绉嶅瓙 INPUT CHANNELS 3 杈撳叆鏁版嵁鐨勬尝娈垫暟锛孯GB锛屼负3 OUTPUT MASK CHANNELS 1 瀹氫箟杈撳嚭mask鐨勬尝娈垫暟锛屽彧鏈変
  • String与StringBuffer的区别

    String 是一个常量 即一旦创建不可更改 输出结果为 helloworldjeok 看似 string变量name的值改变了 其实此name非彼name 输出结果为 sex hello worldjeok name hello worl
  • ocaml学习随笔-1

    utop let rec my listprint items match items with first the rest gt printf s n first my listprint the rest gt val my list
  • React入门笔记(二)

    React入门笔记 二 1 前情回顾 2 组件 3 视频教程地址 1 前情回顾 书接上回 React入门笔记 一 主要介绍React的基本特点 虚拟dom的实现原理 利用包管理工具搭建基本的React单页面引用等 如果你跳过了前面的项目配置
  • Regular Expression实现

    主要分2大块 核心部分 就是一个NFA 只支持标准正则的操作 concatenation union iteration 限定上限的iteration 对应的meta character只有 upper 扩展部分 这部分是把扩展正则表达式转