うわー、それは可能であることがわかりました(醜いですが)!
時間がない場合や、説明全体をわざわざ読むことができない場合は、次のコードを使用してください。
$str = '0 1 2 3 4 5 6 7 8 9 10 11 12 13 19 20 29 99 100 139';
$str = preg_replace("/\d+/", "$0~", $str);
$str = preg_replace("/$/", "#123456789~0", $str);
do
{
$str = preg_replace(
"/(?|0~(.*#.*(1))|1~(.*#.*(2))|2~(.*#.*(3))|3~(.*#.*(4))|4~(.*#.*(5))|5~(.*#.*(6))|6~(.*#.*(7))|7~(.*#.*(8))|8~(.*#.*(9))|9~(.*#.*(~0))|~(.*#.*(1)))/s",
"$2$1",
$str, -1, $count);
} while($count);
$str = preg_replace("/#123456789~0$/", "", $str);
echo $str;
それでは始めましょう。
したがって、まず、他の人が述べたように、ループしても(対応する増分を1桁に挿入する方法のため)、1回の置換では不可能です。ただし、最初に文字列を準備すると、ループできる単一の置換があります。これがPHPを使用した私のデモ実装です。
私はこのテスト文字列を使用しました:
$str = '0 1 2 3 4 5 6 7 8 9 10 11 12 13 19 20 29 99 100 139';
まず、マーカー文字を追加して、インクリメントするすべての数字にマークを付けましょう(私はを使用~
しますが、ターゲット文字列では絶対に発生しないクレイジーなUnicode文字またはASCII文字シーケンスを使用する必要があります。
$str = preg_replace("/\d+/", "$0~", $str);
一度に(右から左に)数字ごとに1桁を置き換えるので、完全な数字ごとにそのマーキング文字を追加するだけです。
ここに主なハックがあります。文字列の最後に小さな「ルックアップ」を追加します(文字列には含まれない一意の文字で区切られます。簡単にするために使用しまし#
た)。
$str = preg_replace("/$/", "#123456789~0", $str);
これを使用して、数字を対応する後継者に置き換えます。
ここでループが発生します。
do
{
$str = preg_replace(
"/(?|0~(.*#.*(1))|1~(.*#.*(2))|2~(.*#.*(3))|3~(.*#.*(4))|4~(.*#.*(5))|5~(.*#.*(6))|6~(.*#.*(7))|7~(.*#.*(8))|8~(.*#.*(9))|9~(.*#.*(~0))|(?<!\d)~(.*#.*(1)))/s",
"$2$1",
$str, -1, $count);
} while($count);
さて、何が起こっているのですか?一致するパターンには、考えられるすべての桁に対して1つの選択肢があります。これにより、数字が後継者にマップされます。たとえば、最初の選択肢を考えてみましょう。
0~(.*#.*(1))
これは、0
後に続く増分マーカー~
と一致し、次にチート区切り文字と対応する後続文字までのすべてに一致します(これが、すべての数字をそこに配置する理由です)。置換を一瞥すると、これはに置き換えられます$2$1
(これは、その後、元に戻すため1
に一致したすべてのものになります~
)。プロセスでを削除することに注意してください~
。0
からへの数字をインクリメントする1
だけで十分です。番号は正常にインクリメントされ、持ち越しはありません。
次の8つの選択肢は、の数字がまったく同じ1
です8
。次に、2つの特殊なケースを処理します。
9~(.*#.*(~0))
を置き換える場合9
、増分マーカーを削除せず、0
代わりに結果の左側に配置します。これ(周囲のループと組み合わせる)は、キャリーオーバー伝搬を実装するのに十分です。現在、1つの特別なケースが残っています。9
sのみで構成されるすべての番号について~
は、番号の前にが表示されます。それが最後の選択肢です。
(?<!\d)~(.*#.*(1))
~
数字が前に付いていない(したがって、ネガティブルックビハインド)に遭遇した場合、それは数字全体に渡されている必要があるため、単に。に置き換え1
ます。ネガティブな後ろ姿も必要ないと思いますが(これがチェックされる最後の選択肢であるため)、この方法の方が安全だと感じます。
(?|...)
パターン全体の周りの短いメモ。$1
これにより、$2
(文字列の下のこれまでにない大きな数字の代わりに)同じ参照内で代替の2つの一致が常に見つかるようになります。
DOTALL
最後に、修飾子( )を追加して、s
改行を含む文字列でこれが機能するようにします(そうでない場合は、最後の行の数値のみがインクリメントされます)。
これにより、かなり単純な置換文字列になります。最初に(後継者と、場合によってはキャリーオーバーマーカーをキャプチャした)書き込み$2
を行ってから、一致した他のすべてをで元の場所に戻し$1
ます。
それでおしまい!文字列の最後からハックを削除するだけで、完了です。
$str = preg_replace("/#123456789~0$/", "", $str);
echo $str;
> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 20 21 30 100 101 140
したがって、これは完全に正規表現で行うことができます。そして、私たちが持っている唯一のループは常に同じ正規表現を使用しています。これは、を使用せずに取得できる限り近いと思いますpreg_replace_callback()
。
もちろん、文字列に小数点付きの数値がある場合、これは恐ろしいことをします。しかし、それはおそらく最初の準備-交換によって対処することができます。
更新:私は、このアプローチが(だけでなく)任意の増分にすぐに拡張されることに気づきました+1
。最初の交換を変更するだけです。追加する数は~
、すべての数に適用する増分と同じです。それで
$str = preg_replace("/\d+/", "$0~~~", $str);
文字列内のすべての整数を。ずつインクリメントします3
。