Apakah ada cara untuk mencapai persamaan tampilan negatif di ekspresi reguler javascript? Saya perlu mencocokkan string yang tidak dimulai dengan kumpulan karakter tertentu.
Sepertinya saya tidak dapat menemukan regex yang melakukan ini tanpa gagal jika bagian yang cocok ditemukan di awal string. Penampilan negatif tampaknya menjadi satu-satunya jawaban, tetapi javascript tidak memilikinya.
EDIT: Ini adalah regex yang ingin saya kerjakan, tetapi tidak:
(?<!([abcdefg]))m
Jadi itu akan cocok dengan 'm' di 'jim' atau 'm', tapi bukan 'jam'
javascript
regex
negative-lookbehind
Andrew Ensley
sumber
sumber
(?:[^abcdefg]|^)(m)
? Seperti di"mango".match(/(?:[^abcdefg]|^)(m)/)[1]
Jawaban:
Lookbehind Pernyataan mendapat diterima ke dalam spesifikasi ECMAScript pada 2018.
Penggunaan lookbehind positif:
console.log( "$9.99 €8.47".match(/(?<=\$)\d+(\.\d*)?/) // Matches "9.99" );
Penggunaan tampilan negatif di belakang:
console.log( "$9.99 €8.47".match(/(?<!\$)\d+(?:\.\d*)/) // Matches "8.47" );
Dukungan platform:
sumber
Sejak 2018, Lookbehind Assertions adalah bagian dari spesifikasi bahasa ECMAScript .
// positive lookbehind (?<=...) // negative lookbehind (?<!...)
Jawab sebelum 2018
Karena Javascript mendukung lookahead negatif , salah satu cara untuk melakukannya adalah:
membalikkan string masukan
cocokkan dengan ekspresi reguler terbalik
membalikkan dan memformat ulang korek api
const reverse = s => s.split('').reverse().join(''); const test = (stringToTests, reversedRegexp) => stringToTests .map(reverse) .forEach((s,i) => { const match = reversedRegexp.test(s); console.log(stringToTests[i], match, 'token:', match ? reverse(reversedRegexp.exec(s)[0]) : 'Ø'); });
Contoh 1:
Mengikuti pertanyaan @ andrew-ensley:
test(['jim', 'm', 'jam'], /m(?!([abcdefg]))/)
Keluaran:
jim true token: m m true token: m jam false token: Ø
Contoh 2:
Mengikuti komentar @neaumusic (cocok
max-height
tapi tidakline-height
, tokennyaheight
):test(['max-height', 'line-height'], /thgieh(?!(-enil))/)
Keluaran:
max-height true token: height line-height false token: Ø
sumber
max-height
tetapi tidakline-height
dan saya hanya ingin pertandingan menjadiheight
''(?!\()
akan menggantikan tanda kutip''(''test'''''''test
dari ujung yang lain, sehingga meninggalkan(''test'NNNtest
daripada(''testNNN'test
.Misalkan Anda ingin menemukan semua yang
int
tidak didahului olehunsigned
:Dengan dukungan untuk melihat ke belakang negatif:
Tanpa dukungan untuk melihat ke belakang negatif:
((?!unsigned ).{9}|^.{0,8})int
Pada dasarnya idenya adalah untuk mengambil n karakter sebelumnya dan mengecualikan kecocokan dengan pandangan ke depan negatif, tetapi juga mencocokkan kasus di mana tidak ada karakter n sebelumnya. (di mana n adalah panjang tampilan ke belakang).
Jadi regex yang dimaksud:
akan diterjemahkan ke:
Anda mungkin perlu bermain dengan kelompok penangkap untuk menemukan tempat yang tepat dari string yang menarik minat Anda atau Anda ingin mengganti bagian tertentu dengan bagian lain.
sumber
"So it would match the 'm' in 'jim' or 'm', but not 'jam'".replace(/(j(?!([abcdefg])).|^)m/g, "$1[MATCH]")
pengembalian"So it would match the 'm' in 'ji[MATCH]' or 'm', but not 'jam'"
Ini sangat sederhana dan berhasil!Strategi Mijoja berfungsi untuk kasus spesifik Anda tetapi tidak secara umum:
js>newString = "Fall ball bill balll llama".replace(/(ba)?ll/g, function($0,$1){ return $1?$0:"[match]";}); Fa[match] ball bi[match] balll [match]ama
Berikut adalah contoh di mana tujuannya adalah untuk mencocokkan double-l tetapi tidak jika diawali dengan "ba". Perhatikan kata "balll" - true lookbehind seharusnya menyembunyikan 2 l pertama tetapi cocok dengan pasangan kedua. Tapi dengan mencocokkan 2 l pertama dan kemudian mengabaikan kecocokan itu sebagai positif palsu, mesin ekspresi reguler melanjutkan dari akhir pertandingan itu, dan mengabaikan karakter apa pun dalam positif palsu.
sumber
Menggunakan
newString = string.replace(/([abcdefg])?m/, function($0,$1){ return $1?$0:'m';});
sumber
newString
akan selalu samastring
. Mengapa begitu banyak suara positif?"Jim Jam Momm m".replace(/([abcdefg])?m/g, function($0, $1){ return $1 ? $0 : '[match]'; });
. Ini harus kembaliJi[match] Jam Mo[match][match] [match]
. Tetapi perhatikan juga bahwa seperti yang disebutkan Jason di bawah ini, ini dapat gagal pada kasus edge tertentu.Anda dapat menentukan grup non-penangkap dengan meniadakan kumpulan karakter Anda:
... yang akan cocok dengan setiap
m
TIDAK yang didahului oleh salah satu huruf itu.sumber
(?:[^a-g]|^)m
. Lihat regex101.com/r/jL1iW6/2 untuk menjalankan contoh.Ini adalah bagaimana saya mencapai
str.split(/(?<!^)@/)
Node.js 8 (yang tidak mendukung lookbehind):str.split('').reverse().join('').split(/@(?!$)/).map(s => s.split('').reverse().join('')).reverse()
Bekerja? Ya (unicode belum teruji). Tidak menyenangkan? Iya.
sumber
mengikuti ide Mijoja, dan menggambar dari masalah yang diungkapkan oleh JasonS, saya punya ide ini; saya memeriksa sedikit tetapi tidak yakin pada diri saya sendiri, jadi verifikasi oleh seseorang yang lebih ahli daripada saya di js regex akan sangat bagus :)
var re = /(?=(..|^.?)(ll))/g // matches empty string position // whenever this position is followed by // a string of length equal or inferior (in case of "^") // to "lookbehind" value // + actual value we would want to match , str = "Fall ball bill balll llama" , str_done = str , len_difference = 0 , doer = function (where_in_str, to_replace) { str_done = str_done.slice(0, where_in_str + len_difference) + "[match]" + str_done.slice(where_in_str + len_difference + to_replace.length) len_difference = str_done.length - str.length /* if str smaller: len_difference will be positive else will be negative */ } /* the actual function that would do whatever we want to do with the matches; this above is only an example from Jason's */ /* function input of .replace(), only there to test the value of $behind and if negative, call doer() with interesting parameters */ , checker = function ($match, $behind, $after, $where, $str) { if ($behind !== "ba") doer ( $where + $behind.length , $after /* one will choose the interesting arguments to give to the doer, it's only an example */ ) return $match // empty string anyhow, but well } str.replace(re, checker) console.log(str_done)
keluaran pribadi saya:
prinsipnya adalah memanggil
checker
setiap titik dalam string di antara dua karakter mana pun, kapan pun posisi itu merupakan titik awal dari:--- setiap substring dari ukuran apa yang tidak diinginkan (di sini
'ba'
, dengan demikian..
) (jika ukuran itu diketahui; jika tidak, mungkin akan lebih sulit untuk dilakukan)--- --- atau lebih kecil dari itu jika itu adalah awal dari string:
^.?
dan, setelah ini,
--- apa yang sebenarnya dicari (di sini
'll'
).Pada setiap panggilan
checker
, akan ada tes untuk memeriksa apakah nilai sebelumnyall
bukan yang tidak kita inginkan (!== 'ba'
); jika itu masalahnya, kita memanggil fungsi lain, dan itu harus yang ini (doer
) yang akan membuat perubahan pada str, jika tujuannya adalah yang ini, atau lebih umum, yang akan memasukkan data yang diperlukan untuk diproses secara manual hasil pemindaianstr
.di sini kita mengubah string jadi kita perlu menyimpan jejak perbedaan panjang untuk mengimbangi lokasi yang diberikan
replace
, semua dihitungstr
, yang dengan sendirinya tidak pernah berubah.karena string primitif tidak dapat diubah, kita dapat menggunakan variabel
str
untuk menyimpan hasil dari seluruh operasi, tetapi saya pikir contoh, yang sudah diperumit oleh penggantian, akan lebih jelas dengan variabel lain (str_done
).Saya rasa dalam hal kinerja itu pasti cukup keras: semua penggantian tidak berguna dari '' ke '',
this str.length-1
kali, ditambah di sini penggantian manual oleh pelaku, yang berarti banyak pemotongan ... mungkin dalam kasus khusus di atas yang bisa dikelompokkan, dengan memotong string hanya sekali-potong sekitar di mana kita ingin memasukkan[match]
dan.join()
ing dengan[match]
sendirinya.hal lain adalah bahwa saya tidak tahu bagaimana cara menangani kasus yang lebih kompleks, yaitu, nilai kompleks untuk lookbehind palsu ... panjangnya mungkin data yang paling bermasalah untuk didapatkan.
dan, dalam
checker
kasus beberapa kemungkinan nilai yang tidak diinginkan untuk $ di belakang, kita harus mengujinya dengan regex lain (untuk di-cache (dibuat) di luarchecker
adalah yang terbaik, untuk menghindari objek regex yang sama dibuat pada setiap panggilan untukchecker
) untuk mengetahui apakah itu yang ingin kita hindari atau tidak.harap saya sudah jelas; jika tidak, jangan ragu, saya akan mencoba lebih baik. :)
sumber
Menggunakan case Anda, jika Anda ingin mengganti
m
dengan sesuatu, misalnya mengubahnya menjadi huruf besarM
, Anda dapat meniadakan set dalam kelompok penangkap.cocok
([^a-g])m
, ganti dengan$1M
"jim jam".replace(/([^a-g])m/g, "$1M") \\jiM jam
([^a-g])
akan mencocokkan karakter apa pun yang tidak (^
) dalama-g
jangkauan, dan menyimpannya di grup penangkap pertama, sehingga Anda dapat mengaksesnya dengan$1
.Jadi kita menemukan
im
dijim
dan menggantinya denganiM
yang hasil dalamjiM
.sumber
Seperti yang disebutkan sebelumnya, JavaScript memungkinkan lookbehinds sekarang. Di browser lama, Anda masih membutuhkan solusi.
Saya yakin kepala saya tidak ada cara untuk menemukan regex tanpa lookbehind yang memberikan hasil dengan tepat. Yang dapat Anda lakukan hanyalah bekerja dengan kelompok. Misalkan Anda memiliki regex
(?<!Before)Wanted
, di manaWanted
regex yang ingin Anda cocokkan danBefore
regex yang menghitung apa yang tidak boleh mendahului pencocokan. Hal terbaik yang dapat Anda lakukan adalah meniadakan regexBefore
dan menggunakan regexNotBefore(Wanted)
. Hasil yang diinginkan adalah kelompok pertama$1
.Dalam kasus Anda
Before=[abcdefg]
yang mudah dinegasikanNotBefore=[^abcdefg]
. Jadi regexnya adalah[^abcdefg](m)
. Jika Anda membutuhkan posisiWanted
, Anda harus mengelompokkanNotBefore
juga, sehingga hasil yang diinginkan adalah kelompok kedua.Jika kecocokan
Before
pola memiliki panjang tetapn
, yaitu, jika pola tidak berisi token berulang, Anda dapat menghindari meniadakanBefore
pola dan menggunakan ekspresi reguler(?!Before).{n}(Wanted)
, tetapi tetap harus menggunakan grup pertama atau menggunakan ekspresi reguler(?!Before)(.{n})(Wanted)
dan menggunakan yang kedua kelompok. Dalam contoh ini, polaBefore
sebenarnya memiliki panjang tetap, yaitu 1, jadi gunakan regex(?![abcdefg]).(m)
atau(?![abcdefg])(.)(m)
. Jika Anda tertarik dengan semua pertandingan, tambahkang
bendera, lihat cuplikan kode saya:function TestSORegEx() { var s = "Donald Trump doesn't like jam, but Homer Simpson does."; var reg = /(?![abcdefg])(.{1})(m)/gm; var out = "Matches and groups of the regex " + "/(?![abcdefg])(.{1})(m)/gm in \ns = \"" + s + "\""; var match = reg.exec(s); while(match) { var start = match.index + match[1].length; out += "\nWhole match: " + match[0] + ", starts at: " + match.index + ". Desired match: " + match[2] + ", starts at: " + start + "."; match = reg.exec(s); } out += "\nResulting string after statement s.replace(reg, \"$1*$2*\")\n" + s.replace(reg, "$1*$2*"); alert(out); }
sumber
Ini secara efektif melakukannya
"jim".match(/[^a-g]m/) > ["im"] "jam".match(/[^a-g]m/) > null
Cari dan ganti contoh
"jim jam".replace(/([^a-g])m/g, "$1M") > "jiM jam"
Perhatikan bahwa string lihat-balik negatif harus sepanjang 1 karakter agar berfungsi.
sumber
"m".match(/[^a-g]m/)
yeildsnull
juga. Saya ingin "m" dalam kasus itu juga./(?![abcdefg])[^abcdefg]m/gi
ya ini tipuan.sumber
(?![abcdefg])
ini benar-benar berlebihan, karena[^abcdefg]
sudah melakukan tugasnya untuk mencegah karakter tersebut cocok.