转换或数字包含括号的公式

本文关键字:包含括 数字 转换 | 更新日期: 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();
    }
}