Найти и заменить RegEx поиском с подстановочными знаками и добавлением значения - PullRequest
0 голосов
/ 11 мая 2010

Приведенный ниже код взят из моих других вопросов, которые я задавал здесь, на SO. Все были очень полезны, и я почти понимаю RegEx, но столкнулся с другим препятствием.


Это то, что мне нужно сделать в двух словах. Мне нужно взять эту строку, которая находится в текстовом файле, который я загружаю в переменную содержимого:


X17.8Y-1.Z0.1G0H1E1


Мне нужно сделать поиск по шаблону для значения X, значения Y, значения Z и значения H. Когда я закончу, мне нужно записать это обратно в мой текстовый файл (я знаю, как создать текстовый файл, чтобы это не было проблемой).


X17.8Y-1.G54G0T2
G43Z0.1H1M08


У меня есть код, который мне дали добрые пользователи, за исключением того, что мне нужно создать значение T в конце первой строки, использовать значение из H и увеличить его на 1 для значения T. Например:


X17.8Y-1.Z0.1G0H5E1

будет переводиться как:

X17.8Y-1.G54G0 T6
G43Z0.1H5M08

Значение T равно 6, потому что значение H равно 5.



У меня есть код, который делает все (выполняет две функции RegEx и разделяет строку кода на две новые строки и добавляет некоторые новые значения G). Но я не знаю, как добавить значение T обратно в первую строку и увеличить его на 1 от значения H. Вот мой код:

  StreamReader reader = new StreamReader(fDialog.FileName.ToString());
  string content = reader.ReadToEnd();
  reader.Close();

  content = Regex.Replace(content, @"X[-\d.]+Y[-\d.]+", "$0G54G0");
  content = Regex.Replace(content, @"(Z(?:\d*\.)?\d+)[^H]*G0(H(?:\d*\.)?\d+)\w*", "\nG43$1$2M08"); //This must be created on a new line



Этот код прекрасно работает при взятии:

X17.8Y-1.Z0.1G0H5E1

и превращаем его в:

X17.8Y-1.G54G0
G43Z0.1H5M08



но мне нужно, чтобы это превратилось в:


X17.8Y-1.G54G0 * * T6 тысяча пятьдесят-одна * 1 052 *
G43Z0.1H5M08

(обратите внимание, что значение T добавляется к первой строке, которая является значением H +1 (T = H + 1).

Может кто-нибудь изменить мое заявление в RegEx, чтобы я мог сделать это автоматически? Я пытался объединить свои два оператора RegEx в одну строку, но я потерпел неудачу.


Update1 : Стивен в комментариях ниже предлагает: «В регулярном выражении нет арифметических операторов, вам нужно использовать группу, чтобы извлечь значение H, превратить его в int, добавить одно и построить новую строку. Но я понятия не имею, как это сделать в коде C #.

Ответы [ 2 ]

1 голос
/ 11 мая 2010

Самый простой способ сделать это с помощью простой программы, которая использует несколько шаблонов Regex, которые захватывают (именованные) группы, у меня было немного свободного времени, так что вы идете:

Program.cs

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            const string InputFileName = @"input.txt";
            const string OutputFileName = @"output.txt";

            List<Line> parsedLineList = new List<Line>();

            using (StreamReader sr = new StreamReader(InputFileName))
            {
                string inputLine;
                int lineNum = 0;

                while ((inputLine = sr.ReadLine()) != null)
                {
                    lineNum++;

                    Line parsedLine = new Line(inputLine);

                    if (parsedLine.IsMatch)
                    {
                        parsedLineList.Add(parsedLine);
                    }
                    else
                    {
                        Debug.WriteLine("Line {0} did not match pattern {1}", lineNum, inputLine);
                    }
                }
            }

            using (StreamWriter sw = new StreamWriter(OutputFileName))
            {
                foreach (Line line in parsedLineList)
                {
                    sw.WriteLine(line.ToString());
                }
            }
        }
    }
}

С input.txt , содержащим:

X17.8Y-1.Z0.1G0H1E1

, эта программа создает output.txt , содержащий:

X17.8Y-1.G54G0T2G43Z0.1H1M08

Приведенный выше код в Program.cs требует следующих простых определений классов Line и Fragment:

Line.cs

namespace Fragments
{
    class Line
    {
        private readonly static Regex Pattern =
            new Regex(@"^(?<X>X[^Y]+?)(?<Y>Y[^Z]+?)(?<Z>Z[^G]+?)(?<G>G[^H]+?)(?<H>H[^E]+?)(?<E>E[^$])$");

        public readonly string OriginalText;

        public string Text
        {
            get
            {
                return this.X.ToString() + this.Y.ToString() + this.G54.ToString() + this.G.ToString() + this.T.ToString() + Environment.NewLine +
                       this.G43.ToString() + this.Z.ToString() + this.H.ToString() + this.M08.ToString();
            }
        }

        public readonly bool IsMatch;

        public Fragment X { get; set; }
        public Fragment Y { get; set; }
        public readonly Fragment G54 = new Fragment("G54");
        public Fragment G { get; set; }
        public Fragment T { get; set; }
        public readonly Fragment G43 = new Fragment("G43");
        public Fragment Z { get; set; }
        public Fragment H { get; set; }
        public readonly Fragment M08 = new Fragment("M08");
        public Fragment E { get; set; }

        public Line(string text)
        {
            this.OriginalText = text;
            Match match = Line.Pattern.Match(text);
            this.IsMatch = match.Success;

            if (match.Success)
            {
                this.X = new Fragment(match.Groups["X"].Value);
                this.Y = new Fragment(match.Groups["Y"].Value);
                this.G = new Fragment(match.Groups["G"].Value);
                this.Z = new Fragment(match.Groups["Z"].Value);
                this.H = new Fragment(match.Groups["H"].Value);
                this.E = new Fragment(match.Groups["E"].Value);

                this.T = new Fragment('T', this.H.Number + 1.0);
            }
        }

        public override string ToString()
        {
            return this.Text;
        }
    }
}

Fragment.cs

namespace Fragments
{
    class Fragment
    {
        private readonly static Regex Pattern =
            new Regex(@"^(?<Letter>[A-Z]{1})(?<Number>.+)$");

        public readonly string Text;
        public readonly bool IsMatch;

        public readonly char Letter;
        public readonly double Number;

        public Fragment(string text)
        {
            this.Text = text;
            Match match = Fragment.Pattern.Match(text);
            this.IsMatch = match.Success;

            if (match.Success)
            {
                this.Letter = match.Groups["Letter"].Value[0];
                string possibleNumber = match.Groups["Number"].Value;

                double parsedNumber;
                if (double.TryParse(possibleNumber, out parsedNumber))
                {
                    this.Number = parsedNumber;
                }
                else
                {
                    Debug.WriteLine("Couldn't parse double from input {0}", possibleNumber);
                }
            }
            else
            {
                Debug.WriteLine("Fragment {0} did not match fragment pattern", text);
            }
        }

        public Fragment(char letter, double number)
        {
            this.Letter = letter;
            this.Number = number;
            this.Text = letter + number.ToString();
            this.IsMatch = true;
        }

        public override string ToString()
        {
            return this.Text;
        }
    }
}

Создайте новый проект консольного приложения C #, добавьте эти три файла, обновите операторы использования, и вы готовы к работе.Вы можете очень легко изменить код в Program.cs, чтобы прочитать имена входных и выходных файлов из аргументов командной строки Main, чтобы сделать программу многократно используемой.

0 голосов
/ 11 мая 2010

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

System.Text.StringBuilder content = new System.Text.StringBuilder();
using (var reader = new StreamReader(fDialog.FileName.ToString()))
{
    string line = reader.ReadLine();
    while (line != null)
    {
        var matchingExpression = Regex.Match(line, @"(X[-\d.]+)(Y[-\d.]+)(Z(?:\d*\.)?\d+)[^H]*G0H((?:\d*\.)?\d+)\w*");

        content.AppendFormat(
            System.Globalization.CultureInfo.InvariantCulture,
            "{0}{1}G54G0T{2}\n",
            matchingExpression.Groups[0].Value,
            matchingExpression.Groups[1].Value,
            Int32.Parse(matchingExpression.Groups[3].Value) + 1);
        content.AppendFormat(
            System.Globalization.CultureInfo.InvariantCulture,
            "G43{0}H{1}M08\n", 
            matchingExpression.Groups[2].Value, 
            matchingExpression.Groups[3].Value);

        line = reader.ReadLine();
    }
}

И чтобы получить строку вывода, вы должны сделать:

content.ToString();
...