2017-10-19 32 views
1

der folgende Code Gegeben:Power regex Problem mit Power 2 vs. Power 5 Zeilenende Anker

$inputString = "`r`n#cmakedefine BREAK_THE_CODE`r`n" 
$match = '(?m)^(.*?)#cmakedefine(.*?)$' 
$replace = 'hello $1#undef$2 goodbye ' 
$retVal = $inputString -replace $match,$replace -join "`r`n" 
Write-Host $retVal 

Powershell 5 erzeugt den folgenden (erwarteten) Ausgang:

hello #undef BREAK_THE_CODE goodbye 

2 erzeugt die Powershell folgende (unerwartete) Ausgabe:

goodbye def BREAK_THE_CODE 

Wer weiß warum? Oder habe einen Workaround. Mir geht es gut mit [Regex ::], wenn es mir mehr Konsistenz kauft. Ich brauche beide Versionen, um die gleichen Ergebnisse zu erzielen. Für das, was es wert ist, werden Hallo und Auf Wiedersehen am Ende /* und */ sein, aber ich versuche zu vermeiden, das Problem mit irgendetwas zu verwechseln, das als ein Sonderzeichen interpretiert werden könnte.

EDIT: Matts Antwort unten ist Detaillierung, dass beide Versionen Powershell die gleiche Leistung produzieren, [CR][LF]hello #undef BREAK_THE_CODE[CR] goodbye [LF] und den scheinbaren Unterschied zwischen den Versionen ist eigentlich ISE vs. nicht ISE und Write-Host. Das Hauptproblem ist hier, dass meine Regex gerade gebrochen wurde, obwohl ich immer noch nicht weiß, warum das so ist.

Ich landete mit:

$match = "(?m)^(.*?)#cmakedefine([^\r\n]*)" 
$replace = '/* $1#undef$2 */' 

Als kleine Seitenleiste, die Hilfe für den VS 2015 IDE (die fast auf jeden Fall das gleiche Regex-Engine verwendet) listet \r?$ als das eigentliche Ende der Zeile Ankers: https://msdn.microsoft.com/en-us/library/2k3te2cs.aspx

Dieser Anker könnte gemacht werden, indem er den \r Teil von ihm fängt, den ich anfänglich tat, aber ich bevorzugte Wiktors Lösung unten.

+1

Sie können erheblich verbessern '(. *?) (\ 'r?)' - siehe meine Antwort wie. –

Antwort

4

Dies hat nichts mit Regex oder PS-Version zu tun, sondern mit, wie Write-Host ist mit dieser einsamen Wagenrücklauf in der Mitte der Zeichenfolge behandelt. Ich kann das auch in PowerShell 2.0 und 5.0 aber nicht ISE reproduzieren, da es eine andere Umgebung ist. Sie können Ihre Ergebnisse auf diese Aussage reduzieren.

write-host "Hello Matt`rBagel" 

Es sollte „Hallo Matt Bagel“ gedruckt, sondern druckt „Bagel Matt“ der Wagenrücklauf setzt die Host-Cursor-Position und dann weiter schreiben. Es hat also Hallo Matt gedruckt aber dann "Hallo" mit "Bagel" überschrieben.

Wikipedias definition of CR wird dazu beitragen zu erklären, dass dies der Status von Entwurf ist, wenn das Steuerzeichen selbst ist.

ein Wagenrücklauf, die manchmal als Kartusche Rückkehr bekannt und oft CR verkürzt oder Gegenzug ist ein Steuerzeichen oder einen Mechanismus verwendete ein Gerät Position zu Beginn einer Textzeile setzen.

Sie würden dies nicht sehen, wenn Sie nur Write-Output verwenden oder einfach nur $retVal auf der Kommandozeile. Das Entfernen der einsamen CR würde das auch beheben.

write-host ("Hello Matt`rBagel" -replace "`r(?<!`n)") 

Der Grund, warum Sie dieses Problem haben, ist, dass es Ihre (.*?)$ erscheint verankert auf "`n" so die Capture-Gruppe ist die Wagenrücklauf raubend. Wenn dies etwas ist, das Sie in Ihren realen Daten sehen, müssen Sie es irgendwie berücksichtigen.

+0

Ahh, ausgezeichnet. Dies ist hilfreiche Information, obwohl ich leider in einer Funktion bin und Write-Output verschluckt wird. – zzxyz

+0

dann ersetzen Sie die einsame CR ....... – Matt

+0

Das ist also das andere Problem. Ich bin mir nicht sicher, warum mein regulärer Ausdruck gebrochen ist :) – zzxyz

2

Das Problem mit Ihrem '(?m)^(.*?)#cmakedefine(.*?)$' Muster ist, dass das . Symbol jedes Zeichen übereinstimmt, aber eine LF (ein Newline-only), und (?m) Modifikator macht das $ Ankerspiel direkt vor dem LF, nach einer möglichen CR. Das heißt, ist das CR-Symbol, die am Ende der Gruppe Capturing 2.

Sehen Sie sich Ihren string:

<CR><LF> 
#cmakedefine BREAK_THE_CODE<CR><LF> 
|------------- MATCH ---------| 
||   |-----Group2------| 

Gruppe 1 Wert ist leer, Gruppe 2 enthält BREAK_THE_CODE. So wird die CR "einsam" wie Matt explains.

Hier ist eine mögliche Lösung, das letzte lazy Punktmuster mit [^\r\n]* ersetzen, die 0 oder mehr Zeichen anders als CR und LF entspricht (und $ wird überflüssig dann):

$match = '(?m)^(.*?)#cmakedefine([^\r\n]*)' 
$replace = 'hello $1#undef$2 goodbye ' 

Hier ist ein regex demo (leider , alle Werte in der Tabelle sind von Whitespace getrimmt, aber es muss jetzt gut funktionieren.)

+0

Ist das ein typisches Problem mit Zeilenenden? Ich kann mich nicht erinnern, dass ich mit anderen Regex-Engines darauf gestoßen bin, aber vielleicht habe ich jedes Mal mit Linux-Zeilenenden gearbeitet. – zzxyz

+0

.NET ist nicht das einzige. Z.B. [Python macht das gleiche] (https://ideone.com/4O9fDD). Es ist jedoch [nicht der Fall mit JS] (https://jsfiddle.net/x4t16au3/). –