Panduan Pasukan Lucee MY

Regex dalam CFML: reFind, reReplace dan perangkapnya

Panduan regex CFML untuk reFind, reFindNoCase, reMatch dan reReplace, termasuk kumpulan tangkapan, bendera, dan perangkap garis miring berganda yang kerap.

Regex dalam CFML: reFind, reReplace dan perangkapnya
0
Kandungan

reFind() dalam CFML berjalan di atas enjin Apache ORO, bukannya java.util.regex dan bukan juga PCRE. reFind memulangkan kedudukan padanan pertama atau sifar, reMatch memulangkan semua padanan, dan reReplace menggantikan teks dengan rujukan balik. Artikel ini membawa anda melalui ketiga-tiganya, bendera sebaris, dan perangkap garis miring berganda yang paling kerap menggigit pembangun CFML.

Enjin di sebalik reFind: Apache ORO, bukan java.util.regex

Ramai menganggap regex CFML memakai enjin Java kerana Lucee berjalan di atas JVM. Anggapan itu salah. Jejak ralat sebenar menunjukkan kelas org.apache.oro.text.regex.Perl5Compiler. Lucee memakai Apache Jakarta ORO, enjin regex gaya Perl 5 yang lebih tua dan lebih terhad daripada java.util.regex.

Dialek yang anda boleh tulis bergantung kepada enjin, bukan bahasa. Ciri dalam PCRE atau java.util.regex tidak semestinya wujud di sini. Bendera (?U), kumpulan bernama (?<nama>...), kumpulan atomik (?>...), dan lihat belakang (?<=...) semuanya tidak dikenali ORO dan melempar MalformedPatternException. Semua fakta ini diuji pada Lucee 7.1, bukan dibaca daripada dokumentasi.

reFind dan reFindNoCase: kedudukan atau sifar

reFind menerima corak dahulu, kemudian rentetan, kemudian dua hujah pilihan: kedudukan mula (lalai 1, berasaskan 1) dan returnSubExpressions. Ia memulangkan kedudukan padanan pertama sebagai integer, atau sifar jika tiada padanan. reFindNoCase sama sahaja tetapi tidak peka huruf besar kecil.

<cfscript>
    writeOutput(reFind("\d", "abc123"));        // 4: digit pertama pada kedudukan 4
    writeOutput(reFind("\d", "abcdef"));        // 0: tiada padanan
    writeOutput(reFind("abc", "XABCX"));        // 0: peka huruf besar kecil
    writeOutput(reFindNoCase("abc", "XABCX"));  // 2: tidak peka huruf
</cfscript>

Kedudukan yang dipulangkan berasaskan 1, bukan 0, dan sifar bermaksud "tiada padanan". Ini berbeza daripada bahasa yang memulangkan indeks 0, jadi if (reFind(pola, teks)) sah kerana sifar ialah palsu dalam CFML.

Kumpulan tangkapan dan returnSubExpressions

Hujah keempat reFind ialah boolean returnSubExpressions. Apabila true, ia memulangkan struktur dengan tiga kekunci: pos, len dan match. pos dan len ialah tatasusunan berasaskan 1, match pula tatasusunan teks padanan.

Indeks 1 sentiasa padanan keseluruhan; indeks 2 dan seterusnya mengikut urutan kurungan buka.

<cfscript>
    hasil = reFind("(\d+)-(\d+)", "harga 12-34", 1, true);
    writeOutput(hasil.pos[1]);   // 7: permulaan keseluruhan padanan "12-34"
    writeOutput(hasil.len[1]);   // 5: panjang keseluruhan padanan
    writeOutput(hasil.pos[2]);   // 7: permulaan kumpulan 1 "12"
    writeOutput(hasil.pos[3]);   // 10: permulaan kumpulan 2 "34"
    writeOutput(hasil.match[3]); // "34": teks kumpulan 2
</cfscript>

Ini cara paling mudah untuk memecahkan satu padanan kepada bahagian, contohnya tarikh atau pengecam berformat. Untuk semua padanan, gunakan reMatch.

reMatch dan reMatchNoCase: semua padanan sekaligus

reMatch memulangkan tatasusunan semua padanan. Urutan hujahnya sama dengan reFind: corak dahulu, kemudian rentetan.

<cfscript>
    padanan = reMatch("\d+", "a1b22c333");
    writeOutput(serializeJSON(padanan)); // ["1","22","333"]

    padanan2 = reMatchNoCase("abc", "ABCabcAbC");
    writeOutput(serializeJSON(padanan2)); // ["ABC","abc","AbC"]
</cfscript>

Guna reMatch untuk semua padanan, dan reFind dengan returnSubExpressions untuk padanan pertama beserta kumpulan dalamannya.

reReplace dan reReplaceNoCase: rujukan balik dan susunan hujah

Di sinilah perangkap paling halus. reReplace mengambil rentetan dahulu, kemudian corak, gantian, dan skop. Ini terbalik daripada reFind dan reMatch. Menulis reReplace(corak, rentetan, gantian) memberi hasil yang tidak masuk akal tanpa sebarang ralat.

Rujukan balik dalam gantian memakai \1, \2 dan seterusnya. Skop lalai ialah one, jadi gunakan all untuk semua padanan.

<cfscript>
    // reReplace(rentetan, corak, gantian, skop): rentetan DAHULU
    writeOutput(reReplace("12-34", "(\d+)-(\d+)", "\2/\1", "all")); // 34/12
    writeOutput(reReplace("2026-10-06", "(\d{4})-(\d{2})-(\d{2})", "\3/\2/\1", "all")); // 06/10/2026
    writeOutput(reReplace("a1 b2 c3", "\d", "X", "one")); // aX b2 c3
    writeOutput(reReplaceNoCase("ABC x abc", "abc", "X", "all")); // X x X
</cfscript>

Contoh kedua menyusun semula tarikh ISO kepada format hari dahulu. Corak menangkap tahun, bulan dan hari, lalu gantian \3/\2/\1 menyusun semula ketiga-tiganya.

Bendera sebaris: (?i), (?m), (?s) dan (?U)

Bendera sebaris ditulis di awal corak. Tiga bendera yang berfungsi dalam ORO ialah (?i) untuk tidak peka huruf, (?m) untuk berbilang baris, dan (?s) untuk titik padan baris baharu.

<cfscript>
    writeOutput(reFind("(?i)abc", "XABCX"));  // 2: tidak peka huruf
    writeOutput(reFind("(?m)^baris", "satu" & chr(10) & "baris dua")); // 6: ^ padan awal baris
    writeOutput(serializeJSON(reMatch("<.+?>", "<a>dan<b>"))); // ["<a>","<b>"]: kuantifier malas

    try {
        reFind("(?U)<.+>", "<a>dan<b>");
    } catch (any e) {
        writeOutput(e.message); // Sequence (?U...) not recognized
    }
</cfscript>

(?U) wujud dalam PCRE sebagai mod tidak tamak global, tetapi ORO tidak mengenalinya dan melempar MalformedPatternException dengan mesej Sequence (?U...) not recognized. Cara tidak tamak yang betul ialah kuantifier malas *?, +? dan ??.

Satu lagi kejutan yang disahkan: dalam ORO, titik . memadankan baris baharu secara lalai, jadi reFind("a.b", "a" & chr(10) & "b") memulangkan 1 tanpa (?s). Ini berbeza daripada java.util.regex yang memerlukan DOTALL. Bendera (?s) masih diterima tetapi hampir tiada kesan.

Perangkap sebenar yang perlu dijaga

Satu garis miring, bukan dua

Ini perangkap paling mahal untuk pembangun daripada Java atau JavaScript. Dalam Java, anda menulis "\\d" untuk menghasilkan corak \d. Dalam CFML, garis miring belakang bukan aksara larikan, jadi anda menulis "\d" terus. "\\d" mengandungi dua garis miring dan mencari garis miring harfiah, bukan digit.

<cfscript>
    // CFML tidak melarikan diri dengan garis miring, jadi satu garis miring sudah betul.
    satu = "\d";   // 2 aksara: garis miring + d, menjadi corak \d
    dua  = "\\d";  // 3 aksara: dua garis miring + d, menjadi corak \\d

    writeOutput(len(satu));          // 2
    writeOutput(reFind(satu, "5"));  // 1: padan digit
    writeOutput(reFind(dua, "5"));   // 0: tidak padan digit
    writeOutput(reFind(dua, "\d"));  // 1: padan teks literal garis miring + d
</cfscript>

Tanpa sebarang ralat, reFind(dua, "5") memulangkan 0. Corak nampak betul tetapi tidak pernah memadankan apa yang anda maksudkan. Peraturannya mudah: tulis satu garis miring dalam rentetan CFML, dan sahkan dengan len() apabila ragu-ragu.

Padanan tamak lawan malas

Kuantifier * dan + tamak secara lalai, jadi ia memadankan seberapa banyak yang mungkin. Bandingkan reMatch("<.+>", "<a>dan<b>") yang memulangkan "<a>dan<b>" dengan reMatch("<.+?>", "<a>dan<b>") yang memulangkan ["<a>","<b>"]. Tambah ? selepas kuantifier untuk padanan terkecil.

Saiz input dan ReDoS

ORO ialah enjin lama tanpa had masa bina dalam. Corak dengan kuantifier bersarang seperti (a+)+ boleh mencetuskan pengunduran berulang yang meledak secara eksponen. Pada input yang cukup panjang, satu permintaan boleh membeku, dan ini tidak dapat dihadkan dari dalam reFind.

Pertahanan praktikal: semak panjang input dahulu, elak kuantifier bersarang, dan pilih corak mudah. Jangan biarkan corak daripada input pengguna dijalankan tanpa kawalan. Rujuk OWASP untuk penerangan penuh.

Perbezaan PCRE dan Java

Disahkan pada Lucee 7.1: ORO menyokong sempadan perkataan \b, lihat hadapan (?=...) dan (?!...), kumpulan bukan tangkap (?:...), kelas aksara \d \w \s, kuantifier {n,m}, alternasi |, dan rujukan balik. Ia tidak menyokong (?U), lihat belakang (?<=...), kumpulan bernama (?<nama>...), dan kumpulan atomik (?>...). Setiap yang tidak disokong melempar MalformedPatternException, jadi ralatnya kelihatan, tetapi hanya semasa runtime.

Jadual pola biasa

Pola di bawah ialah titik permulaan yang telah diuji, bukan pengesahan menyeluruh.

KeperluanPolaContoh sahContoh tidak sah
E-mel ringkas^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ali@contoh.mybukan-emel
Telefon Malaysia^\+?6?01[0-9]-?[0-9]{7,8}$012-345678912345
Slug^[a-z0-9]+(-[a-z0-9]+)*$regex-cfml-refindRegex CFML
Tarikh ISO^\d{4}-\d{2}-\d{2}$2026-10-0606/10/2026

Amaran penting: pola e-mel ini hanya menyemak bentuk kasar. Ia tidak mengesahkan kewujudan domain dan tidak menjamin alamat boleh menerima mel. Untuk pengesahan sebenar, hantar e-mel pengesahan. Corak tarikh ISO menyemak bentuk, bukan kesahihan kalendar, jadi 2026-13-99 masih lulus.

Cara menguji pola dengan selamat

Bangunkan fungsi kecil yang membalut reFindNoCase dalam try/catch supaya corak tidak sah ditangkap sebagai ralat boleh dibaca, bukan halaman 500.

<cfscript>
    function ujiPola(pola, teks) {
        try {
            writeOutput(pola & " => " & reFindNoCase(pola, teks) & "<br>");
        } catch (any e) {
            writeOutput(pola & " RALAT: " & e.message & "<br>");
        }
    }
    ujiPola("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$", "ali@contoh.my");
    ujiPola("^\+?6?01[0-9]-?[0-9]{7,8}$", "+6012-3456789");
    ujiPola("^[a-z0-9]+(-[a-z0-9]+)*$", "regex-cfml-refind");
    ujiPola("^\d{4}-\d{2}-\d{2}$", "2026-10-06");
</cfscript>

Corak yang gagal pada sintaks ORO muncul sebagai MalformedPatternException, bukan kegagalan senyap. Ini berguna semasa penyahpepijatan CFML dalam VS Code. Untuk kerja rentetan yang lebih luas, rujuk panduan fungsi rentetan CFML. Apabila regex membersihkan input yang sampai ke penyemak imbas, patuhi panduan keselamatan web XSS dan CSRF, dan untuk corak yang menyentuh URL, semak panduan SEO teknikal CFML.

Soalan Lazim (FAQ)

Apakah enjin regex yang dipakai oleh reFind dalam Lucee?

Lucee memakai Apache Jakarta ORO, enjin regex gaya Perl 5. Ia bukan java.util.regex dan bukan PCRE, jadi sesetengah ciri moden seperti kumpulan bernama dan (?U) tidak tersedia.

Apakah perbezaan reFind dan reFindNoCase?

reFind peka huruf besar kecil, manakala reFindNoCase tidak. Selain itu kedua-duanya memulangkan kedudukan padanan pertama atau sifar, dan kedua-duanya menyokong returnSubExpressions.

Kenapa corak saya tidak memadankan digit walaupun saya tulis dua garis miring?

Kerana dalam CFML garis miring belakang bukan aksara larikan. "\\d" menghasilkan dua garis miring dalam corak, jadi ia mencari garis miring harfiah. Tulis "\d" dengan satu garis miring sahaja.

Bagaimana cara mengambil semua padanan dan bukan hanya yang pertama?

Guna reMatch atau reMatchNoCase. Ia memulangkan tatasusunan semua padanan. reFind pula hanya memulangkan padanan pertama.

Adakah regex e-mel mencukupi untuk mengesahkan alamat e-mel?

Tidak. Pola e-mel hanya menyemak bentuk kasar. Satu-satunya pengesahan yang boleh dipercayai ialah menghantar e-mel pengesahan kepada pemilik alamat itu.

Sumber

Senarai semak

  • Ingat enjin di sebalik regex CFML ialah Apache ORO, bukan java.util.regex atau PCRE.
  • Tulis satu garis miring sahaja untuk regex dalam rentetan CFML, contohnya "\d", dan sahkan dengan len().
  • Semak susunan hujah reReplace: rentetan dahulu, kemudian corak, kemudian gantian.
  • Guna returnSubExpressions dengan indeks berasaskan 1 untuk kumpulan tangkapan.
  • Guna reMatch untuk semua padanan, dan kuantifier malas *? +? untuk tidak tamak.
  • Elak (?U), lihat belakang, kumpulan bernama dan kumpulan atomik kerana ORO tidak menyokongnya.
  • Hadkan panjang input sebelum regex untuk mengelakkan ReDoS.
  • Balut reFind dalam try/catch semasa menguji corak supaya ralat enjin kelihatan.
Bab 11 daripada 13

Asas CFML untuk Pemula

Log masuk untuk menyimpan kemajuan bacaan anda.

0 komen

0 komen

Log masuk untuk menjawab atau memberi komen.

Log masuk