转换或数字包含括号的公式
本文关键字:包含括 数字 转换 | 更新日期: 2023-09-27 18:30:41
我需要找到一种方法来确认处理过的公式(仅使用数字,字母和括号)例如,对于此输入:"5(2(a)sz)
",输出应为 :"aaszaaszaaszaaszaasz
"我以这种方式尝试:
string AddChainDeleteBracks(int open, int close, string input)
{
string to="",from="";
//get the local chain multipule the number in input[open-1]
//the number of the times the chain should be multiplied
for (int i = input[open - 1]; i > 0; i--)
{
//the content
for (int m = open + 1; m < close; m++)
{
to = to + input[m];
}
}
//get the chain i want to replace with "to"
for (int j = open - 1; j <= close; j++)
{
from = from + input[j];
}
String output = input.Replace(from, to);
return output;
}
但它不起作用。你有更好的主意来解决这个问题吗?
您可以将左括号位置以及与该括号关联的数字存储在堆栈中(Last-in-First-Out,例如System.Collections.Generic.Stack); 然后,当您遇到第一个(即:下一个)右括号时,弹出堆栈的顶部:这将为您提供子字符串的开始和结束位置(到目前为止最内)括号之间的您需要重复。 然后将原始字符串的这一部分(包括重复编号)替换为重复的字符串。 继续,直到到达字符串的末尾。
需要注意的事项:
- 当您进行替换时,您需要更新当前位置,以便它现在指向新(修改的)字符串中重复字符串的末尾
- 根据是否允许 0 重复,您可能需要处理空重复 - 这是一个空字符串
- 当您到达字符串的末尾时,堆栈应为空(所有左括号都与右括号匹配)
- 堆栈可能会在字符串中间变为空 - 如果遇到右括号,则表示输入字符串格式不正确
- 可能有一种方法可以转义左/右括号,因此它们不算作重复模式的一部分 - 这取决于您的要求
由于表达式的语法是递归的,我建议使用递归方法。
首先将表达式拆分为单个标记。 我使用Regex
来执行此操作并删除空条目。
示例:"5(2(a)sz)"
拆分为"5", "(", "2", "(", "a", ")", "sz", ")"
使用枚举器可以逐个获取令牌。 tokens.MoveNext()
获取下一个令牌。 tokens.Current
是当前令牌。
public string ConvertExpression(string expression)
{
IEnumerator<string> tokens = Regex.Split(expression, @"'b")
.Where(s => s != "")
.GetEnumerator();
if (tokens.MoveNext()) {
return Parse(tokens);
}
return "";
}
这里的主要工作以递归方式完成
private string Parse(IEnumerator<string> tokens)
{
string s = "";
while (tokens.Current != ")") {
int n;
if (tokens.Current == "(") {
if (tokens.MoveNext()) {
s += Parse(tokens);
if (tokens.Current == ")") {
tokens.MoveNext();
return s;
}
}
} else if (Int32.TryParse(tokens.Current, out n)) {
if (tokens.MoveNext()) {
string subExpr = Parse(tokens);
var sb = new StringBuilder();
for (int i = 0; i < n; i++) {
sb.Append(subExpr);
}
s += sb.ToString();
}
} else {
s += tokens.Current;
if (!tokens.MoveNext())
return s;
}
}
return s;
}
的第二个答案。我的第一个答案是快速拍摄。在这里,我尝试通过一一做这些事情来创建解析器。
为了转换表达式,您需要解析它。这意味着您必须分析其语法。在分析其语法时,您也可以生成输出。
1 首先要做的是定义所有有效表达式的语法。
在这里,我使用 EBNF 来做到这一点。EBNF很简单。
{
和}
将重复项括起来(可能为零)。
[
和]
包含一个可选部件。
|
将替代方案分开。
有关EBNF的更多详细信息,请参阅Wikpedia上的扩展Backus-Naur形式(EBNF)。(此处使用的 EBNF 变体删除了串联运算符",")。
我们在 EBNF 中的语法
表达式 = { 术语 }。项 = [ 数字 ] 因素。因子 = 文本 |"("表达式")" |术语。
例子
5(2(a)sz) => aaszaaszaaszaaszaasz 5(2a sz) => aaszaaszaaszaaszaasz 2 3(a 2b)c => abbabbababbbabbbc
2 词法分析
在分析语法之前,我们必须将整个表达式拆分为单个词汇标记(数字、运算符等)。我们使用enum
来指示令牌类型
private enum TokenType
{
None,
LPar,
RPar,
Number,
Text
}
以下字段用于保存令牌信息和布尔_error
,用于指示解析期间是否发生错误。
private IEnumerator<Match> _matches;
TokenType _tokenType;
string _text;
int _number;
bool _error;
ConvertExpression
开始转换的方法。它将表达式拆分为表示为 Regex.Matches
的单个标记。这些被方法使用GetToken
,这反过来又将Regex.Matches
转换为更有用的信息。此信息存储在上述字段中。
public string ConvertExpression(string expression)
{
_matches = Regex.Matches(expression, @"'d+|'(|')|[a-zA-Z]+")
.Cast<Match>()
.GetEnumerator();
_error = false;
return GetToken() ? Expression() : "";
}
private bool GetToken()
{
_number = 0;
_tokenType = TokenType.None;
_text = null;
if (_error || !_matches.MoveNext())
return false;
_text = _matches.Current.Value;
switch (_text[0]) {
case '(':
_tokenType = TokenType.LPar;
break;
case ')':
_tokenType = TokenType.RPar;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
_tokenType = TokenType.Number;
_number = Int32.Parse(_text);
break;
default:
_tokenType = TokenType.Text;
break;
}
return true;
}
3 句法和语义分析
现在,我们有了执行实际解析和表达式转换所需的一切。下面的每个方法都分析一个 EBNF 语法生成,并以字符串形式返回转换结果。将 EBNF 转换为 C# 代码非常简单。语法中的重复将转换为 C# 循环语句。选项将转换为if
语句,替代项将转换为switch
语句。
// Expression = { Term }.
private string Expression()
{
string s = "";
do {
s += Term();
} while (_tokenType != TokenType.RPar && _tokenType != TokenType.None);
return s;
}
// Term = [ Number ] Factor.
private string Term()
{
int n;
if (_tokenType == TokenType.Number) {
n = _number;
if (!GetToken()) {
_error = true;
return " Error: Factor expected.";
}
string factor = Factor();
if (_error) {
return factor;
}
var sb = new StringBuilder(n * factor.Length);
for (int i = 0; i < n; i++) {
sb.Append(factor);
}
return sb.ToString();
}
return Factor();
}
// Factor = Text | "(" Expression ")" | Term.
private string Factor()
{
switch (_tokenType) {
case TokenType.None:
_error = true;
return " Error: Unexpected end of Expression.";
case TokenType.LPar:
if (GetToken()) {
string s = Expression();
if (_tokenType == TokenType.RPar) {
GetToken();
return s;
} else {
_error = true;
return s + " Error ')' expected.";
}
} else {
_error = true;
return " Error: Unexpected end of Expression.";
}
case TokenType.RPar:
_error = true;
GetToken();
return " Error: Unexpected ')'.";
case TokenType.Text:
string t = _text;
GetToken();
return t;
default:
return Term();
}
}