Какое регулярное выражение может выбрать имена столбцов Excel в формуле в C #? - PullRequest
3 голосов
/ 13 мая 2009

Я обязуюсь реализовать автозаполнение формулы Excel в C #.

Давайте предположим, что эта формула находится в B100:

=SUM($B$99:B99)

Я хочу изменить эту формулу на C100:

=SUM($B$99:C99)

Эта формула является лишь примером. Вот некоторые реальные примеры:

=(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}

(на самом деле {0} и {1} числа)

Что мне нужно сделать , вообще говоря, это выбрать эти имена столбцов и "увеличить" их. Имена столбцов, окруженные $ в формулах, не должны обновляться.

Как отождествить эти поля с регулярным выражением?

Ответы [ 3 ]

3 голосов
/ 13 мая 2009

Вот регулярное выражение, которое имеет дело только с формулами. Я оставлю материал Excel для вас. При условии, что у вас есть коллекция строк, представляющих ваши формулы, вы можете выполнить их через это, чтобы увеличить имена столбцов.

Некоторые комментарии:

  • ** Проверьте это тщательно! ** Возможно, сделайте лист вручную и сравните ваши усилия с полученными результатами.
  • Это не должно случайно изменять имена функций, которые соответствуют шаблону именования ячеек. Если вы знаете, что в ваших формулах есть имена функций Excel, которые включают числа, следите за ними и - опять же - ** проверяйте результаты **.
  • Регулярное выражение не проверяет, что вы кормите, это формула - я так понимаю, вы используете только формулы. Другими словами, я не проверял, чтобы строка начиналась со знака "=". Если вы планируете передавать не-формулы через это для других значений ячеек, то добавьте проверку, где IsMatch используется в ветви if с использованием Formula.StartsWith ("="). Чтобы понять, о чем я говорю, добавьте к моему образцу дополнительную тестовую строку, например «Проверить генерацию T4» - если проверка StartsWith («=») не выполнена, она будет соответствовать, и T4 станет U4.

Шаблон регулярного выражения был на самом деле легкой частью. Он будет соответствовать любой последовательности букв и цифр и игнорирует типы ячеек $ A $ 1 и $ A1. Сложной частью была логика увеличения столбца. Я добавил комментарии, чтобы прояснить этот момент, поэтому возьмите немного кофе и прочитайте его:)

Я уверен, что это может быть улучшено, но это то, на что у меня было время.

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;
}

Результаты должны выглядеть следующим образом (для краткости я убрал разбивку ячеек):

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
0 голосов
/ 13 мая 2009

+! для автоматизации Excel и выполнения работы там.

Однако, если вы хотите сделать это в C #, вы можете начать здесь http://ewbi.blogs.com/develops/2004/12/excel_formula_p.html. Может быть, когда вы переварите все правила для токенизации формулы, вы сможете создать RE .

0 голосов
/ 13 мая 2009

Ты уверен, что не усложняешь, не так ли? Это то, что Excel делает изначально. Пример: выделите ячейку B100 в приведенном выше примере. Обратите внимание на контур ячейки, что есть маленький черный ящик в правом нижнем углу ячейки. Это позволяет вам автозаполнения. Нажмите на этот черный ящик и перетащите его вправо (в ячейку C100). У вас должно быть только автозаполнение над одним столбцом, а у C100 должно быть = SUM ($ B $ 99: C99). И если вы вместо этого потянете вниз, вы получите = SUM ($ B $ 99: B100).

Если ваша цель - повторить это поведение в C #, я думаю, что лучший способ сделать это - выяснить, как подключиться к функциям автозаполнения Excel. Я не знаю точно, как бы вы сделали это в C #, но, безусловно, они доступны в VBA (и вы можете просто записать макрос, выполнить описанные выше шаги, а затем посмотреть на сгенерированный код, чтобы увидеть код автозаполнения VBA ).

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...