3 Stimmen

Welcher reguläre Ausdruck kann Excel-Spaltennamen in einer Formel in C# auswählen?

Ich bin nedding zu implementieren Excel-Formel Autofill in C#.

Nehmen wir an, diese Formel liegt bei B100:

=SUM($B$99:B99)

Ich möchte diese Formel bei C100 anders gestalten:

=SUM($B$99:C99)

Diese Formel ist nur ein Beispiel. Einige echte Beispiele sind:

=(SUM($B${0}:B{0})/SUM({1}!$B${0}:{1}!B{0}) -1)

=SUM(B{0}:B{1})

=B{0} + B{1}

=C{0}+ B{1}

=$B${0}+ AC{1}

(man bedenke, dass {0} und {1} in der Tat Zahlen sind)

Was ich tun muss besteht allgemein darin, diese Spaltennamen zu wählen und sie zu "inkrementieren". Spaltennamen, die in Formeln von $ umgeben sind, sollten nicht aktualisiert werden.

Wie kann man diese Felder mit Regex identifizieren?

4voto

Ahmad Mageed Punkte 91261

Hier ist eine Regex-Lösung, die sich ausschließlich mit Formeln befasst. Den Excel-Kram überlasse ich Ihnen. Wenn Sie eine Sammlung von Zeichenketten haben, die Ihre Formeln darstellen, können Sie diese durchlaufen lassen, um Ihre Spaltennamen zu erhöhen.

Einige Kommentare:

  • **Testen Sie dies gründlich!** Machen Sie vielleicht ein Blatt manuell und vergleichen Sie Ihre Bemühungen mit den generierten Ergebnissen.
  • Dadurch sollten nicht versehentlich Funktionsnamen geändert werden, die in das Benennungsmuster der Zelle passen. Wenn Sie wissen, dass Ihre Formeln Excel-Funktionsnamen haben, die Zahlen enthalten, sollten Sie darauf achten und - wieder - die Ergebnisse **überprüfen**.
  • Die Regex prüft nicht, ob es sich bei der Eingabe um eine Formel handelt - ich nehme an, Sie verwenden nur Formeln. Mit anderen Worten, ich habe nicht überprüft, ob die Zeichenkette mit einem "="-Zeichen beginnt. Wenn Sie planen, Nicht-Formeln für andere Zellwerte zu verwenden, fügen Sie eine Prüfung hinzu, bei der IsMatch im if-Zweig mit formula.StartsWith("=") verwendet wird. Um zu verstehen, worauf ich mich beziehe, fügen Sie eine zusätzliche Testzeichenfolge zu meinem Beispiel hinzu, z. B. "Check out T4 generation" - wenn keine StartsWith("=")-Prüfung durchgeführt wird, wird dies übereinstimmen und T4 wird zu U4.

Das Regex-Muster war eigentlich der einfache Teil. Es passt auf jede Buchstaben-Zahlen-Folge und ignoriert die Zelltypen $A$1 und $A1. Der knifflige Teil war die Logik zum Erhöhen der Spalte. Ich habe Kommentare hinzugefügt, um diesen Teil zu verdeutlichen, also nehmen Sie sich einen Kaffee und lesen Sie es durch :)

Ich bin mir sicher, dass man das noch verbessern könnte, aber ich hatte nur Zeit für dieses Beispiel.

using System.Text.RegularExpressions;

static void Main(string[] args)
{
    string[] formulas = { "Z1", "ZZ1", "AZ1", "AZB1", "BZZ2",
                        "=SUM($B$99:B99)","=SUM($F99:F99)", "=(SUM($B$0:B0)/SUM(1!$B$11:22!B33) -1)",
                        "=SUM(X80:Z1)", "=A0 + B1 - C2 + Z5", "=C0+ B1",
                        "=$B$0+ AC1", "=AA12-ZZ34 + AZ1 - BZ2 - BX3 + BZX4",
                        "=SUMX2MY2(A2:A8,B2:B8)",   // ensure function SUMX2MY2 isn't mistakenly incremented
                        "=$B$40 + 50 - 20"          // no match
                        //,"Check out T4 generation!"  // not a formula but it'll still increment T4, use formula.StartsWith("=")
                        };

    // use this if you don't want to include regex comments
    //Regex rxCell = new Regex(@"(?<![$])\b(?<col>[A-Z]+)(?<row>\d+)\b");

    // regex comments in this style requires RegexOptions.IgnorePatternWhitespace
    string rxCellPattern = @"(?<![$])       # match if prefix is absent: $ symbol (prevents matching $A1 type of cells)
                                            # (if all you have is $A$1 type of references, and not $A1 types, this negative look-behind isn't needed)
                            \b              # word boundary (prevents matching Excel functions with a similar pattern to a cell)
                            (?<col>[A-Z]+)  # named capture group, match uppercase letter at least once
                                            # (change to [A-Za-z] if you have lowercase cells)
                            (?<row>\d+)     # named capture group, match a number at least once
                            \b              # word boundary
                            ";
    Regex rxCell = new Regex(rxCellPattern, RegexOptions.IgnorePatternWhitespace);

    foreach (string formula in formulas)
    {
        if (rxCell.IsMatch(formula))
        {
            Console.WriteLine("Formula: {0}", formula);
            foreach (Match cell in rxCell.Matches(formula))
                Console.WriteLine("Cell: {0}, Col: {1}", cell.Value, cell.Groups["col"].Value);

            // the magic happens here
            string newFormula = rxCell.Replace(formula, IncrementColumn);
            Console.WriteLine("Modified: {0}", newFormula);
        }
        else
        {
            Console.WriteLine("Not a match: {0}", formula);
        }
        Console.WriteLine();
    }
}

private static string IncrementColumn(Match m)
{
    string col = m.Groups["col"].Value;
    char c;

    // single character column name (ie. A1)
    if (col.Length == 1)
    {
        c = Convert.ToChar(col);
        if (c == 'Z')
        {
            // roll over
            col = "AA";
        }
        else
        {
            // advance to next char
            c = (char)((int)c + 1);
            col = c.ToString();
        }
    }
    else
    {
        // multi-character column name (ie. AB1)
        // in this case work backwards to do some column name "arithmetic"
        c = Convert.ToChar(col.Substring(col.Length - 1, 1));   // grab last letter of col

        if (c == 'Z')
        {
            string temp = "";
            for (int i = col.Length - 1; i >= 0; i--)
            {
                // roll over should occur
                if (col[i] == 'Z')
                {
                    // prepend AA if current char is not the last char in column and its next neighbor was also a Z
                    // ie. column BZZ: if current char is 1st Z, it's neighbor Z (2nd Z) just got incremented, so 1st Z becomes AA
                    if (i != col.Length - 1 && col[i + 1] == 'Z')
                    {
                        temp = "AA" + temp;
                    }
                    else
                    {
                        // last char in column is Z, becomes A (this will happen first, before the above if branch ever happens)
                        temp = "A" + temp;
                    }
                }
                else
                {
                    temp = ((char)((int)col[i] + 1)).ToString() + temp;
                }
            }
            col = temp;
        }
        else
        {
            // advance char
            c = (char)((int)c + 1);
            // chop off final char in original column, append advanced char
            col = col.Remove(col.Length - 1) + c.ToString();
        }
    }

    // updated column and original row (from regex match)
    return col + m.Groups["row"].Value;
}

Die Ergebnisse sollten wie folgt aussehen (ich habe die Zellaufschlüsselung der Kürze halber entfernt):

Formula: Z1
Modified: AA1

Formula: ZZ1
Modified: AAA1

Formula: AZ1
Modified: BA1

Formula: AZB1
Modified: AZC1

Formula: BZZ2
Modified: CAAA2

Formula: =SUM($B$99:B99)
Modified: =SUM($B$99:C99)

Formula: =SUM($F99:F99)
Modified: =SUM($F99:G99)

Formula: =(SUM($B$0:B0)/SUM(1!$B$11:22!B33) -1)
Modified: =(SUM($B$0:C0)/SUM(1!$B$11:22!C33) -1)

Formula: =SUM(X80:Z1)
Modified: =SUM(Y80:AA1)

Formula: =A0 + B1 - C2 + Z5
Modified: =B0 + C1 - D2 + AA5

Formula: =C0+ B1
Modified: =D0+ C1

Formula: =$B$0+ AC1
Modified: =$B$0+ AD1

Formula: =AA12-ZZ34 + AZ1 - BZ2 - BX3 + BZX4
Modified: =AB12-AAA34 + BA1 - CA2 - BY3 + BZY4

Formula: =SUMX2MY2(A2:A8,B2:B8)
Modified: =SUMX2MY2(B2:B8,C2:C8)

Not a match: =$B$40 + 50 - 20

0voto

Caleb Punkte 11

Sie sind sicher, dass Sie die Sache nicht zu kompliziert machen, oder? Das ist etwas, was Excel von Haus aus kann. Beispiel: Markieren Sie die Zelle B100 im obigen Beispiel. Beachten Sie, dass in der Zelle ein kleiner schwarzer Kasten in der rechten unteren Ecke zu sehen ist. Damit können Sie automatisch ausfüllen. Klicken Sie auf diesen schwarzen Kasten und ziehen Sie ihn nach rechts (zur Zelle C100). Sie sollten gerade eine Spalte automatisch ausgefüllt haben, und C100 sollte =SUMME($B$99:C99) sein. Wenn Sie stattdessen nach unten ziehen, erhalten Sie =SUMME($B$99:B100).

Wenn Ihr Ziel ist es, dieses Verhalten in C# zu wiederholen, würde ich vermuten, dass der beste Weg, dies zu tun ist, um herauszufinden, wie man in Excel AutoFill Funktionen Haken. Ich weiß nicht genau, wie Sie dies in C# tun würden, aber sicherlich sind sie in VBA verfügbar (und Sie können einfach ein Makro aufzeichnen, die oben genannten Schritte ausführen und dann den generierten Code ansehen, um den AutoFill-VBA-Code zu sehen).

Ich hoffe, das hilft.

0voto

Dick Kusleika Punkte 31883

+! für die Automatisierung von Excel und die Erledigung der Arbeit dort.

Wenn Sie es jedoch unbedingt in C# machen wollen, können Sie hier beginnen http://ewbi.blogs.com/develops/2004/12/excel_formula_p.html . Wenn Sie alle Regeln für die Tokenisierung einer Formel kennen, können Sie vielleicht eine RE erstellen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X