Regex: заменить внутреннюю строку - PullRequest
2 голосов
/ 25 марта 2010

Я работаю с файлами EDI X12 (в частности, с 835 для тех из вас, кто работает в сфере здравоохранения), и у меня есть конкретный поставщик, который использует версию, не совместимую с HIPAA (я думаю, 3090).Проблема в том, что в определенном сегменте (опять же, PLB - для тех, кому это нужно) они отправляют код, который больше не поддерживается стандартом HIPAA.Мне нужно найти конкретный код и обновить его исправленным кодом.

Я думаю, что Regex будет лучше для этого, но я все еще очень плохо знаком с Regex, и я не уверен, гденачать.Моя текущая методология заключается в том, чтобы превратить файл в массив строк, найти массив, начинающийся с «PLB», разбить его на массив строк, найти код и изменить его.Как вы можете догадаться, это очень многословный код для чего-то, что должно быть (я думаю) довольно простым.

Вот пример того, что я ищу:

~PLB|1902841224|20100228|49>KC15X078001104|.08~

Ивот что я хочу изменить на:

~PLB|1902841224|20100228|CS>KC15X078001104|.08~

Любые предложения?

ОБНОВЛЕНИЕ: После проверки я обнаружил, что недостаточно четко определил свой вопрос.Вышеприведенная запись является примером, но это не обязательно любое конкретное совпадение форматирования - есть три вещи, которые могут измениться между этой записью и некоторыми другими (в другом файле), которые мне нужно исправить.Это:

  • Канал (|) может быть любым не алфавитно-цифровым символом.Сам файл будет определять, какой символ (обычно это труба или звездочка).
  • Символ> также может быть любым другим не буквенно-цифровым символом (чаще всего: или>)
  • Набор чиселсразу после PLB идет идентификатор, который может меняться по формату и длине.Я только видел там числовые идентификаторы, но технически это мог быть буквенно-цифровой код, и он не обязательно должен был бы быть 10 символами.

Мой план - использовать String.Format () с моим Regexсопоставить строку так, чтобы |и> можно заменить на правильные символы.

И для записи.Да, я ненавижу ANSI X12.

Ответы [ 7 ]

2 голосов
/ 25 марта 2010

Предполагая, что «нарушающий» код всегда 49, вы можете использовать следующее:

resultString = Regex.Replace(subjectString, @"(?<=~PLB|\d{10}|\d{8}|)49(?=>\w+|)", "CS");

Это ищет 49, если это первый элемент после | разделителя, перед которым стоит группа из 8 цифр, еще одна |, группа из 10 цифр, еще одна | и ~PLB , Он также смотрит, следует ли за ним >, затем любое количество буквенно-цифровых символов и еще один |.

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

resultString = Regex.Replace(subjectString, @"(?<=~PLB\1\w+\1\d{8}(\W))49(?=\W\w+\1)", "CS");

Теперь любой не алфавитно-цифровой символ допускается в качестве разделителя вместо | или > (но в случае | он всегда должен быть одним и тем же), и ограничения на количество символов для первое поле было ослаблено.

1 голос
/ 25 марта 2010

Другой, аналогичный подход, который работает с любым действительным файлом X12 для замены одного значения данных другим в соответствующем сегменте:

public void ReplaceData(string filePath, string segmentName, 
    int elementPosition, int componentPosition, 
    string oldData, string newData)
{
    string text = File.ReadAllText(filePath);

    Match match = Regex.Match(text, 
     @"^ISA(?<e>.).{100}(?<c>.)(?<s>.)(\w+.*?\k<s>)*IEA\k<e>\d*\k<e>\d*\k<s>$");

    if (!match.Success)
        throw new InvalidOperationException("Not an X12 file");

    char elementSeparator = match.Groups["e"].Value[0];
    char componentSeparator = match.Groups["c"].Value[0];
    char segmentTerminator = match.Groups["s"].Value[0];

    var segments = text
        .Split(segmentTerminator)
        .Select(s => s.Split(elementSeparator)
            .Select(e => e.Split(componentSeparator)).ToArray())
        .ToArray();

    foreach (var segment in segments.Where(s => s[0][0] == segmentName &&
                              s.Count() > elementPosition &&
                              s[elementPosition].Count() > componentPosition &&
                              s[elementPosition][componentPosition] == oldData))
    {
        segment[elementPosition][componentPosition] = newData;
    }

    File.WriteAllText(filePath,
        string.Join(segmentTerminator.ToString(), segments
        .Select(e => string.Join(elementSeparator.ToString(), 
            e.Select(c => string.Join(componentSeparator.ToString(), c))
             .ToArray()))
        .ToArray()));
}

Используемое регулярное выражение проверяет правильность конверта обмена X12 и гарантирует, что всесегменты в файле содержат как минимум один символьный элемент имени.Он также анализирует разделители элементов и компонентов, а также терминатор сегмента.

0 голосов
/ 25 марта 2010

То, что я нашел, работает так:

parts = original.Split(record);

        for(int i = parts.Length -1; i >= 0; i--)
        {
            string s = parts[i];
            string nString =String.Empty;
            if (s.StartsWith("PLB"))
            {
                string[] elems = s.Split(elem);
                if (elems[3].Contains("49" + subelem.ToString()))
                {
                    string regex = string.Format(@"(\{0})49({1})", elem, subelem);
                    nString = Regex.Replace(s, regex, @"$1CS$2");
                }

Мне все еще приходится разбивать мой исходный файл на набор строк, а затем оценивать каждую строку, но теперь швы будут работать.

Если кто-нибудь знает, как обойти эту строку. Разделитесь наверху, я бы хотел посмотреть образец.

0 голосов
/ 25 марта 2010

Стандарт протокола X12 позволяет указывать разделители элементов и компонентов в заголовке, поэтому все, что жестко закодировано в «|» и символы «>» могут со временем сломаться. Поскольку стандарт требует, чтобы символы, используемые в качестве разделителей (и разделителей сегментов, например «~»), не могли появляться в данных (нет escape-последовательности, позволяющей их встраивать), синтаксический анализ синтаксиса очень прост. Может быть, вы уже делаете что-то похожее на это, но для удобства чтения ...

// The original segment string (without segment terminator):

string segment = "PLB|1902841224|20100228|49>KC15X078001104|.08";

// Parse the segment into elements, then the fourth element
// into components (bounds checking is omitted for brevity):

var elements = segment.Split('|');
var components = elements[3].Split('>');

// If the first component is the bad value, replace it with
// the correct value (again, not checking bounds):

if (components[0] == "49")
    components[0] = "CS";

// Reassemble the segment by joining the components into
// the fourth element, then the elements back into the
// segment string:

elements[3] = string.Join(">", components);
segment = string.Join("|", elements);

Очевидно, что оно более многословно, чем одно регулярное выражение, но анализ файлов X12 так же прост, как разбиение строк на один символ. За исключением заголовка фиксированной длины (который определяет разделители), весь набор транзакций может быть проанализирован с помощью Split:

// Starting with a string that contains the entire 835 transaction set:

var segments = transactionSet.Split('~');
var segmentElements = segments.Select(s => s.Split('|')).ToArray();

// segmentElements contains an array of element arrays,
// each composite element can be split further into components as shown earlier
0 голосов
/ 25 марта 2010

Это будет искать ~ PLB | # | # | в начале и замените 2 числа перед> на CS.

Regex.Replace(testString, @"(?<=~PLB|[0-9]{10}|[0-9]{8})(\|)([0-9]{2})(>)", @"$1CS$3")
0 голосов
/ 25 марта 2010

Вы можете разбить его с помощью регулярных выражений да. Если я правильно понимаю ваш пример 2 символа между | и> должны быть буквы, а не цифры.

~PLB\|\d{10}\|\d{8}\|(\d{2})>\w{14}\|\.\d{2}~

Этот шаблон будет соответствовать старому и захватывать символы между | и>. Что вы можете затем использовать для изменения (поиск в БД или что-то) и сделать замену следующим шаблоном:

(?<=|)\d{2}(?=>)
0 голосов
/ 25 марта 2010

Предполагая, что ваш код всегда представляет собой двузначное число, которое следует после символа канала | и перед знаком больше >, вы можете сделать это следующим образом:

var result = Regex.Replace(yourString, @"(\|)(\d{2})(>)", @"$1CS$3");
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...