代码之家  ›  专栏  ›  技术社区  ›  AllenG

正则表达式:替换内部字符串

  •  2
  • AllenG  · 技术社区  · 14 年前

    我使用的是X12EDI文件(特别是835s,用于医疗保健领域),我有一个特定的供应商使用的是不符合HIPAA的版本(我想是3090)。问题是在一个特定的段(PLB-同样,对于那些关心的人来说)中,他们发送的代码不再受HIPAA标准的支持。我需要找到特定的代码,并用更正的代码更新它。

    我认为正则表达式最好,但我对正则表达式还是很陌生,我不知道从哪里开始。我目前的方法是将文件转换为字符串数组,找到以“PLB”开头的数组,将其分解为字符串数组,找到代码,然后对其进行更改。您可以猜到,这是非常冗长的代码,应该(我认为)相当简单。

    下面是我要找的一个例子:

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

    我想把它改成:

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

    有什么建议吗?

    更新:经过复习,我发现我对问题的定义不够清晰。上面的记录就是一个例子,但它不一定是一个特定的格式匹配-有三件事可能会改变这个记录和其他一些(在另一个文件中)我必须修复。他们是:

    • 管道(|)可能是任何非字母数字字符。文件本身将定义哪个字符(通常是管道或星号)。
    • 紧跟在PLB后面的一组数字是一个标识符,可以改变格式和长度。我只见过数字标识,但技术上它可以是字母数字的,不一定是10个字符。

    我的计划是在正则表达式匹配字符串中使用String.Format(),以便|和>可以替换为正确的字符。

    我要说的是。是的,我讨厌ANSI X12。

    7 回复  |  直到 14 年前
        1
  •  2
  •   Tim Pietzcker    14 年前

    假设“冒犯”的代码总是 49

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

    如果它是 | 分隔符,前面有一组8位数字,另一个 | ,一组10位数字,另一组 | ~PLB . 它还可以查看后面是否有 > ,然后是任意数量的字母数字字符,还有一个 .

    有了新的需求(幸运的巧合是.NET是为数不多的允许lookback内部变量重复的regex风格之一),您可以将其更改为:

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

    | > (但在 它必须始终是同一个),并且对第一个字段的字符数的限制已经放宽。

        2
  •  1
  •   tames    14 年前

    另一种类似的方法,适用于任何有效的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交换信封,并确保文件中的所有段至少包含一个字符的name元素。它还解析出元素和组件分隔符以及段终止符。

        3
  •  0
  •   Klaus Byskov Pedersen    14 年前

    假设您的代码始终是管道字符后面的两位数 | 在大于号之前 > 你可以这样做:

    var result = Regex.Replace(yourString, @"(\|)(\d{2})(>)", @"$1CS$3");
    
        4
  •  0
  •   Fabian    14 年前

    你可以用正则表达式来分解它是的。 如果我正确理解您的示例,|和>之间的两个字符;必须是字母而不是数字。

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

    此模式将匹配旧模式并捕获|和>之间的字符;。然后您可以使用它来修改(在数据库或其他地方查找)并用以下模式进行替换:

    (?<=|)\d{2}(?=>)
    
        5
  •  0
  •   Matt Dearing    14 年前

    这将在开始处查找~PLB |#| | | | |,并替换>之前的2个数字;有CS。

    Regex.Replace(testString, @"(?<=~PLB|[0-9]{10}|[0-9]{8})(\|)([0-9]{2})(>)", @"$1CS$3")
    
        6
  •  0
  •   tames    14 年前

    X12协议标准允许在标头中指定元素和组件分隔符,因此任何硬编码“|”和“>”的内容角色最终会被打破。由于标准要求用作分隔符(和段终止符,例如“~”)的字符不能出现在数据中(没有允许嵌入它们的转义序列),因此解析语法非常简单。也许你已经在做类似的事情了,但是为了可读性。。。

    // 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);
    

    // 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
    
        7
  •  0
  •   AllenG    14 年前

    我发现它的作用如下:

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

    我仍然需要将原始文件拆分为一组字符串,然后对每个字符串求值,但该方法现在仍然有效。

    如果有人知道怎么绕过那根绳子的话。从上面分开,我想看看样品。