Jak zastąpić tylko przechwycone grupy?


196

Mam kod HTML przed i po ciągu:

name="some_text_0_some_text"

Chciałbym zastąpić 0coś w stylu:!NEW_ID!

Więc stworzyłem prosty regex:

.*name="\w+(\d+)\w+".*

Ale nie widzę, jak zastąpić wyłącznie przechwycony blok.

Czy istnieje sposób na zastąpienie przechwyconego wyniku, takiego jak (1 USD), jakimś innym ciągiem?

Wynik byłby:

name="some_text_!NEW_ID!_some_text"

Odpowiedzi:


359

Rozwiązaniem jest dodanie przechwytywania dla poprzedniego i następującego tekstu:

str.replace(/(.*name="\w+)(\d+)(\w+".*)/, "$1!NEW_ID!$3")

76
Pozdrowienia z przyszłości! Twoje rozwiązanie wygląda naprawdę schludnie. Czy możesz wyjaśnić swoją odpowiedź?
Polyducks

21
Nawiasy są używane do tworzenia „grup”, którym następnie przypisuje się indeks base-1, dostępny w zamianie na a $, więc pierwsze słowo (\w+)jest w grupie i staje się $1, środkowa część (\d+)jest drugą grupą (ale otrzymuje ignorowane w zastępowaniu), a trzecia grupa to $3. Kiedy podasz ciąg zastępujący "$1!new_ID!$3", $ 1 i $ 3 zostaną automatycznie zamienione na pierwszą grupę i trzecią grupę, umożliwiając zastąpienie drugiej grupy nowym ciągiem, zachowując otaczający tekst.
mix3d

4
Biorąc to pod uwagę, chociaż rozumiem, JAK to działa, liczyłem na bardziej eleganckie rozwiązanie>. <Niemniej jednak mogę teraz przejść do przodu z moim kodem!
mix3d

9
1) Nawet nie musisz przechwytywać \ d + 2) Dlaczego mówisz, że to nie jest eleganckie? Przechwytywanie ma na celu zachowanie rzeczy, a nie wyrzucenie ich. To, co chcesz zachować, to AROUND \ d +, więc naprawdę ma sens (i jest wystarczająco elegancki), aby uchwycić te otaczające części.
Sir4ur0n

3
Niezłe rozwiązanie. Co jeśli chcemy zastąpić grupy przechwytywania, używając grupy przechwytywania jako podstawy do transformacji? Czy istnieje równie eleganckie rozwiązanie? Obecnie przechowuję przechwycone grupy na liście, zapętlam je i zastępuję grupę przechwytywania przekształconą wartością przy każdej iteracji
sookie

15

Teraz, gdy JavaScript ma wygląd (od ES2018 ), w nowszych środowiskach możesz całkowicie unikać grup w takich sytuacjach. Raczej lookbehind za to, co jest przed grupą ty przechwytywania i uprzedzona do przychodzi później i wymienić tylko !NEW_ID! :

const str = 'name="some_text_0_some_text"';
console.log(
  str.replace(/(?<=name="\w+)\d+(?=\w+")/, '!NEW_ID!')
);

Dzięki tej metodzie pełne dopasowanie jest tylko częścią, którą należy wymienić.

  • (?<=name="\w+)- Szukaj name", a po nim znaków słownych (na szczęście, lookbehinds nie muszą mieć stałej szerokości w JavaScript!)
  • \d+ - Dopasuj jedną lub więcej cyfr - jedyna część wzorca nie znajduje się w otoczeniu, jedyna część łańcucha, która będzie w wynikowym dopasowaniu
  • (?=\w+")- Poszukaj znaków słownych, po których następuje "

Pamiętaj, że lookbehind jest całkiem nowy. Działa w nowoczesnych wersjach V8 (w tym Chrome, Opera i Node), ale nie w większości innych środowisk , przynajmniej jeszcze nie. Chociaż możesz niezawodnie korzystać z lookbehind w Node i we własnej przeglądarce (jeśli działa na nowoczesnej wersji V8), nie jest jeszcze wystarczająco obsługiwana przez losowych klientów (jak na publicznej stronie internetowej).


Właśnie przeprowadziłem szybki test synchronizacji i robi wrażenie, jak ważne są dane wejściowe: jsfiddle.net/60neyop5
Kaiido

Ale jeśli, na przykład, chcę wyodrębnić liczbę, wielokrotność i „odłożyć”, będę musiał także pogrupować \d+, prawda?
Mosh Feu

@MoshFeu Użyj funkcji zamiennika i użyj całego dopasowania, cyfry: zamień drugi parametr na match => match * 2. Cyfry są cały czas zgodne, więc nie ma potrzeby grup
CertainPerformance

Mam cię. Dzięki!
Mosh Feu

2

Niewielką poprawą w odpowiedzi Matthew może być spojrzenie w przyszłość zamiast ostatniej grupy przechwytywania:

.replace(/(\w+)(\d+)(?=\w+)/, "$1!NEW_ID!");

Możesz też podzielić dziesiętnie i dołączyć do nowego identyfikatora w ten sposób:

.split(/\d+/).join("!NEW_ID!");

Przykład / Benchmark tutaj: https://codepen.io/jogai/full/oyNXBX


1

Przy dwóch grupach pojmania byłoby również możliwe; Dodałbym także dwa myślniki, jako dodatkowe lewą i prawą granicę, przed i po cyfrach, a zmodyfikowane wyrażenie wyglądałoby tak:

(.*name=".+_)\d+(_[^"]+".*)

const regex = /(.*name=".+_)\d+(_[^"]+".*)/g;
const str = `some_data_before name="some_text_0_some_text" and then some_data after`;
const subst = `$1!NEW_ID!$2`;
const result = str.replace(regex, subst);
console.log(result);


Jeśli chcesz zbadać / uprościć / zmodyfikować wyrażenie, zostało to wyjaśnione w prawym górnym panelu strony regex101.com . Jeśli chcesz, możesz również obejrzeć w tym linku , jak będzie pasował do niektórych przykładowych danych wejściowych.


RegEx Circuit

jex.im wizualizuje wyrażenia regularne:

wprowadź opis zdjęcia tutaj


0

Prostszą opcją jest przechwycenie cyfr i zastąpienie ich.

const name = 'preceding_text_0_following_text';
const matcher = /(\d+)/;

// Replace with whatever you would like
const newName = name.replace(matcher, 'NEW_STUFF');
console.log("Full replace", newName);

// Perform work on the match and replace using a function
// In this case increment it using an arrow function
const incrementedName = name.replace(matcher, (match) => ++match);
console.log("Increment", incrementedName);

Zasoby

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.