0

我需要找到一种方法来处理公式(仅使用数字、字母和括号),例如,对于这个输入:' 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;
    }

但它不起作用。你有更好的主意来解决这个问题吗?

4

3 回答 3

1

You could store the opening parenthesis positions along with the number associated with that parenthesis in a stack (Last-in-First-out, e.g. System.Collections.Generic.Stack); then when you encounter the first (that is: next) closing parenthesis, pop the top of the stack: this will give you the beginning and ending position of the substring between the (so far most inner) parentheses you need to repeat. Then replace this portion of the original string (including the repetion number) with the repeated string. Continue until you reach the end of the string.

Things to be aware of:

  • when you do the replacement, you will need to update your current position so it now points to the end of the repetiotion string in the new (modified) string
  • depending whether 0 repetion is allowed, you might need to handle an empty repetition -- that is an empty string
  • when you reach the end of the string, the stack should be empty (all opening parentheses were matched with a closing one)
  • the stack might become empty in the middle of the string -- if you encounter a closing parentheses, the input string was malformed
  • there might be a way to escape the opening/cloding parentheses, so they don't count as part of the repetition pattern -- this depends on your requirements
于 2012-04-11T15:09:13.457 回答
0

这是我的第二个答案。我的第一个答案是快速拍摄。在这里,我尝试通过一件一件地做事情来创建一个解析器。

为了转换表达式,您需要对其进行解析。这意味着您必须分析其语法。在分析其语法时,您也可以生成输出。

1 首先要做的是定义所有有效表达式的语法。

在这里,我使用 EBNF 来做到这一点。EBNF 很简单。

{并附}上重复(可能为零)。
[并附]上一个可选部分。
|分离备选方案。

有关 EBNF的更多详细信息,请参阅维基百科上的Extended Backus-Naur Form (EBNF) 。(此处使用的 EBNF 变体删除了连接运算符“,”)。

我们在 EBNF 中的语法

表达式 = { 术语}。
术语 = [ 数字 ] 因子。
因子 = 文本 | "(" 表达式 ")" | 学期。

例子

    5(2(a)sz) => aaszaaszaaszaaszaasz
    5(2a sz) => aaszaaszaaszaaszaasz
    2 3(a 2b)c => abbabbabbabbabbabbc

2 词法分析

在分析语法之前,我们必须将整个表达式拆分为单个词汇标记(数字、运算符等)。我们使用anenum来表示token类型

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();
    }
}
于 2012-04-11T22:03:05.057 回答
0

由于表达式的语法是递归的,我建议使用递归方法。

首先将表达式拆分为单个标记。我曾经Regex这样做并删除空条目。

示例:"5(2(a)sz)"被拆分为"5", "(", "2", "(", "a", ")", "sz", ")"

使用 Enumerator 可以让您一一获取令牌。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;
}
于 2012-04-11T16:35:09.010 回答