1

さて、私は解決すべき非常に明白ですが、明らかに重要な問題を抱えています。

単純な string があるとしますab。今度はとを置き換え
たいので、 になります。abbaba

実際の解決策は、2 つの置換を連続して行うことです。しかし、それからの結果は、順序に応じてaaまたはのいずれかです。bb

明らかに、生産状況では、2 つよりもはるかに複雑な文字列とより多くの置換を処理する必要がありますが、問題は依然として適用されます。

私が思いついたアイデアの 1 つは、何かを交換した位置を保存することでした。しかし、交換用の針が元の針とは異なる長さになるとすぐに、それは私を失望させました.

これは一般的な問題ですが、私は C# を使用しています。ここに私が思いついたいくつかのコードがあります:

string original = "abc";

Regex[] expressions = new Regex[]
{
    new Regex("a"), //replaced by ab
    new Regex("b") //replaced by c
};

string[] replacements = new string[]
{
    "ab",
    "c"
};

for (int i = 0; i < expressions.Length; i++)
    original = expressions[i].Replace(original, replacements[i]);

//Expected result: abcc
//Actual result: accc <- the b is replaced by c in the second pass.

これを解決する簡単な方法はありますか?

4

3 に答える 3

1

これが1つの解決策です。文字列に対してすべての正規表現を試し、最初に一致したものを置換してから、文字列の残りの部分を再帰します。これをより高速で複雑にする必要がある場合はMatches()、最初にすべての権利を要求し、左から右に処理し、Indexes式を長い文字列と短い文字列に置き換えるときにそれらを調整し、重複を捨てることができます。

using System;
using System.IO;
using System.Text.RegularExpressions;

class MultiRegex {

    static String Replace(String text, Regex[] expressions,
            String[] replacements, int start=0)
    {
        // Try matching each regex; save the first match
        Match firstMatch = null;
        int firstMatchingExpressionIndex = -1;
        for (int i = 0; i < expressions.Length; i++) {
            Regex r = expressions[i];
            Match m = r.Match(text, start);
            if (m.Success
                    && (firstMatch == null || m.Index < firstMatch.Index))
            {
                firstMatch = m;
                firstMatchingExpressionIndex = i;
            }
        }

        if (firstMatch == null) {
            /* No matches anywhere */
            return text;
        }

        // Replace text, then recurse
        String newText = text.Substring(0, firstMatch.Index)
            + replacements[firstMatchingExpressionIndex]
            + text.Substring(firstMatch.Index + firstMatch.Length);
        return Replace(newText, expressions, replacements,
                start + replacements[firstMatchingExpressionIndex].Length);
    }

    public static void Main() {

        Regex[] expressions = new Regex[]
        {
            new Regex("a"), //replaced by ab
            new Regex("b") //replaced by c
        };

        string[] replacements = new string[]
        {
            "ab",
            "c"
        };

        string original = "a b c";
        Console.WriteLine(
                Replace(original, expressions, replacements));

        // Should be "baz foo bar"
        Console.WriteLine(Replace("foo bar baz",
                    new Regex[] { new Regex("bar"), new Regex("baz"),
                        new Regex("foo") },
                    new String[] { "foo", "bar", "baz" }));
    }
}

これは以下を出力します:

ab c c
baz foo bar
于 2013-02-05T22:03:38.137 に答える
1

単純な 1 対 1 の変換について話している場合は、char 配列に変換して切り替えを行うのがおそらく理想的ですが、より複雑な置換を探しているようです。

基本的には、中間キャラクターを作成して一時的にマークするのがコツです。実際のコードを示すのではなく、変換後の文字列は次のようになります。

ab
%1b
%1%2
b%2
ba

したがって、基本的には に置き換え%%%から、最初の一致をに置き換え%1ます。それらがすべて完了したら、%1その出力などに置き換え、最後に に置き換え%%ます%

ただし、中間構文が入力を汚染しないことを保証できる場合は問題ありません。それができない場合は、トリックを使用して、奇数の%. (これ%%aは一致しますが%%%a、特別な値を意味するため一致しません%a)

于 2013-02-05T22:06:06.230 に答える
0

文字と文字のみの(\ba\b)一致を表すために使用する場合、一致しません。の場合と同様に、になります。aaabb(\bb\b)

 string original = "a b c";
 Regex[] expressions = new Regex[] {
      // @ sign used to signify a literal string
      new Regex(@"(\ba\b)"), // \b represents a word boundary, between a word and a space
      new Regex(@"(\bb\b)"),
 };
 string[] replacements = new string[] {
      "ab",
      "c"
 };
 for(int i = 0; i < expressions.Length; i++)
      original = expressions[i].Replace(original, replacements[i]);

編集1:質問が一致する文字の間にスペースを入れずに変更され、同じabccものabcが必要でした。正規表現がチェックされる順序を逆にしました。

 Regex[] expressions = new Regex[] {
      new Regex(@"b"), //replaced by c
      new Regex(@"a"), //replaced by ab
 };
 string[] replacements = new string[] {
      "c",
      "ab",
 };

編集2:一致する可変長を反映するように回答が変更されました。これは、チェックするパターンの順序に基づいて一致し、パターンをチェックしてから、新しい文字列に移動します

 string original = "a bc";

 Regex[] expressions = new Regex[] {
      new Regex(@"a"), //replaced by ab
      new Regex(@"b"), //replaced by c
 };

 string[] replacements = new string[] {
      "ab",
      "c",
 };
 string newString = string.Empty;
 string workingString = string.Empty;
 // Position of start point in string
 int index = 0;
 // Length to retrieve
 int length = 1;
 while(index < original.Length) {
      // Retrieve a piece of the string
      workingString = original.Substring(index, length);
      // Whether the expression has been matched
      bool found = false;
      for(int i = 0; i < expressions.Length && !found; i++) {
           if(expressions[i].Match(workingString).Success) {
                // If expression matched, add the replacement value to the new string
                newString += expressions[i].Replace(workingString, replacements[i]);
                // Mark expression as found
                found = true;
           }
      }
      if(!found) {
           // If not found, increase length (check for more than one character patterns)
           length++;
           // If the rest of the entire string doesn't match anything, move the character at **index** into the new string
           if(length >= (original.Length - index)) {
                newString += original.Substring(index, 1);
                index++;
                length = 1;
           }
      }
      // If a match was found, start over at next position in string
      else {
           index += length;
           length = 1;
      }
 }
于 2013-02-05T21:16:36.750 に答える