1. 程式人生 > >[Happy Coding] 一個正則表示式,支援邏輯和關係運算符組成的表示式計算

[Happy Coding] 一個正則表示式,支援邏輯和關係運算符組成的表示式計算

I. 寫一個正則表示式,要求判斷一個數是否滿足以下條件:

>= val1 && < val2 ...

1. val1和va2要求支援浮點數;
2. 支援>, >=, <, <=, =, !=關係運算符;
3. 支援&&, ||, and(不區分大小), or(不區分大小寫)邏輯運算

1) 有效的浮點數的正則表示式如下:
([+-]?\d+(\.\d+)?|0?\.\d+)
支援+/-m.n, m.n, .n, 不支援m.
2) 關係運算符的正則表示式如下:
([><!]=?|=)
3) 邏輯運算子的正則表示式如下:
(&&|\|\||[Aa][Nn][Dd]|[Oo][Rr])

所以綜合起來的正則表示式就是:
^(\s*([><!]=?|=)\s*([+-]?\d+(\.\d+)?|0?\.\d+)\s*)((&&|\|\||[Aa][Nn][Dd]|[Oo][Rr])(\s*([><!]=?|=)\s*([+-]?\d+(\.\d+)?|0?\.\d+)\s*))*

II. 如何用boost::regex來處理這樣的表示式,然後判斷一個是否滿足表示式的要求:
假如判斷數30是否滿足"> 20 && < 40",我們會做一個額外的處理,就是在前面加上"AND",最後要處理的表示式會是"AND > 20 && < 40"。這樣一來我們的正則表示式就可以簡化為:
((&&|\|\||[Aa][Nn][Dd]|[Oo][Rr])(\s*([><!]=?|=)\s*([+-]?\d+(\.\d+)?|0?\.\d+)\s*))+
而且也方便我們從裡面提取出運運算元。


以下CPP程式碼演示了從一個字串中提取出運運算元的過程:
void ParseSpec(const std::string& spec)
{
	m_validSpec = false;
	m_Ops.clear();

	std::string specStr = boost::trim_copy(spec);
	if (specStr.empty())
	{
		m_validSpec = true;
		return;
	}
	// we added "AND" as prefix for consistent handling later
	specStr = "AND " + specStr;
	boost::regex reg(specExp);
	m_validSpec = boost::regex_match(specStr, reg);
	if (!m_validSpec) return;

	reg.set_expression(logicalExp+relationExp);
	boost::smatch what;
	std::string::const_iterator start = specStr.begin(), end = specStr.end();
	while (boost::regex_search(start, end, what, reg))
	{
		std::string opStr = std::string(what[1].first, what[1].second);
		std::transform(opStr.begin(), opStr.end(), opStr.begin(), boost::bind(&std::toupper, _1));

		m_Ops.push_back(std::make_pair(opStr,
			std::make_pair(std::string(what[2].first, what[2].second),
			atof(std::string(what[3].first, what[3].second).c_str()))));
		start = what[0].second;
	}
}


注意reg.set_expression(logicalExp+relationExp);這裡調整的正則表示式應該是一個處理單元(沒有外圍的()組),方便boost::regex_search一個一個的提取出邏輯運運算元和關係運算子。


下面C++程式碼演示瞭如何運用提取出的運運算元,判斷一個數是否滿足表示式
bool IsValueOK(double valueToCheck) const
{
	if (!m_validSpec) return true;

	/*
	Below logic will follow logic in C language
	For example: A && B || C
	If A (true), run B, but skipping run C,
	otherwise, run C, but skipping run B
	*/
	bool ret = true;
	for (BOOST_AUTO(it, m_Ops.begin()); it != m_Ops.end(); ++it)
	{
		if (it->first == "AND" || it->first == "&&")
		{
			if (!ret) continue;	// skip this op, like C
			ret = ret && ::RelationOp(valueToCheck, it->second.second, it->second.first);
		}
		if (it->first == "OR" || it->first == "||")
		{
			if (ret) continue;	// skip this op, like C
			ret = ret || ::RelationOp(valueToCheck, it->second.second, it->second.first);
		}
	}
	return ret;
}

以上程式碼模仿邏輯運算AND/OR,滿足短路原則:遇到AND,前面計算結果為FALSE,跳過此次運算;遇到OR,前面計算結果為TRUE,跳過此次運算。