Προγραμματισμός

Κανονικές εκφράσεις σε Java, Μέρος 1: Αντιστοίχιση μοτίβου και κλάση μοτίβου

Οι χαρακτήρες της Java και οι διάφορες κλάσεις συμβολοσειρών προσφέρουν υποστήριξη χαμηλού επιπέδου για αντιστοίχιση μοτίβων, αλλά αυτή η υποστήριξη συνήθως οδηγεί σε σύνθετο κώδικα. Για απλούστερη και αποτελεσματικότερη κωδικοποίηση, η Java προσφέρει το Regex API. Αυτό το σεμινάριο δύο μερών σας βοηθά να ξεκινήσετε με κανονικές εκφράσεις και το Regex API. Πρώτα θα αποσυσκευάσουμε τις τρεις ισχυρές τάξεις που βρίσκονται στο java.util.regex πακέτο, τότε θα εξερευνήσουμε το Πρότυπο τάξη και τις εξελιγμένες κατασκευές που ταιριάζουν με μοτίβα.

λήψη Λήψη του κώδικα Λήψη του πηγαίου κώδικα για παράδειγμα εφαρμογές σε αυτό το σεμινάριο. Δημιουργήθηκε από τον Jeff Friesen για το JavaWorld.

Τι είναι οι κανονικές εκφράσεις;

ΕΝΑ κοινή έκφραση, επίσης γνωστό ως regex ή regexp, είναι μια συμβολοσειρά της οποίας πρότυπο (πρότυπο) περιγράφει ένα σύνολο συμβολοσειρών. Το μοτίβο καθορίζει ποιες χορδές ανήκουν στο σύνολο. Ένα μοτίβο αποτελείται από κυριολεκτικούς χαρακτήρες και μεταχαρακτήρες, που είναι χαρακτήρες που έχουν ειδικό νόημα αντί για κυριολεκτική σημασία.

Ταίριασμα μοτίβου είναι η διαδικασία αναζήτησης κειμένου για αναγνώριση αγώνεςή συμβολοσειρές που ταιριάζουν με το μοτίβο ενός regex. Η Java υποστηρίζει την αντιστοίχιση μοτίβων μέσω του Regex API της. Το API αποτελείται από τρεις κατηγορίες--Πρότυπο, Ταιριαστή, και PatternSyntaxException- όλα βρίσκονται στο java.util.regex πακέτο:

  • Πρότυπο αντικείμενα, επίσης γνωστά ως μοτίβα, είναι μεταγλωττισμένοι regexes.
  • Ταιριαστή αντικείμενα, ή ταιριαστές, είναι μηχανές που ερμηνεύουν μοτίβα για τον εντοπισμό αγώνων ακολουθίες χαρακτήρων (αντικείμενα των οποίων οι κλάσεις εφαρμόζουν το java.lang.CharSequence διεπαφή και χρησιμεύει ως πηγές κειμένου).
  • PatternSyntaxException τα αντικείμενα περιγράφουν παράνομα μοτίβα regex.

Η Java παρέχει επίσης υποστήριξη για αντιστοίχιση μοτίβων μέσω διαφόρων μεθόδων σε αυτήν java.lang.String τάξη. Για παράδειγμα, boolean αγώνες (String regex) επιστρέφει αλήθεια μόνο εάν η συμβολοσειρά κλήσης ταιριάζει ακριβώς regexΤο regex.

Μέθοδοι ευκολίας

Στα παρασκήνια, αγώνες () και ΣειράΟι άλλες μέθοδοι ευκολίας που είναι προσανατολισμένες στο regex εφαρμόζονται από την άποψη του API Regex.

RegexDemo

Έχω δημιουργήσει το RegexDemo εφαρμογή για την επίδειξη των τακτικών εκφράσεων της Java και των διαφόρων μεθόδων που βρίσκονται στο Πρότυπο, Ταιριαστή, και PatternSyntaxException τάξεις. Εδώ είναι ο πηγαίος κώδικας για την επίδειξη:

Λίστα 1. Επίδειξη παλινδρόμησης

εισαγωγή java.util.regex.Matcher; εισαγωγή java.util.regex.Pattern; εισαγωγή java.util.regex.PatternSyntaxException; δημόσια τάξη RegexDemo {public static void main (String [] args) {if (args.length! = 2) {System.err.println ("use: java RegexDemo regex input"); ΕΠΙΣΤΡΟΦΗ; } // Μετατροπή ακολουθιών χαρακτήρων νέας γραμμής (\ n) σε χαρακτήρες νέας γραμμής. args [1] = args [1] .replaceAll ("\ n", "\ n"); δοκιμάστε το {System.out.println ("regex =" + args [0]); System.out.println ("input =" + args [1]); Πρότυπο p = Pattern.compile (args [0]); Matcher m = p.matcher (args [1]); ενώ (m.find ()) System.out.println ("Found [" + m.group () + "] ξεκινώντας από" + m.start () + "και τελειώνοντας στο" + (m.end () - 1)) } catch (PatternSyntaxException pse) {System.err.println ("Bad regex:" + pse.getMessage ()); System.err.println ("Περιγραφή:" + pse.getDescription ()); System.err.println ("Ευρετήριο:" + pse.getIndex ()); System.err.println ("Λανθασμένο μοτίβο:" + pse.getPattern ()); }}}

Το πρώτο πράγμα RegexDemo'μικρό κύριος() Η μέθοδος είναι να επικυρώσει τη γραμμή εντολών της. Αυτό απαιτεί δύο ορίσματα: το πρώτο όρισμα είναι ένα regex και το δεύτερο όρισμα είναι κείμενο εισαγωγής για αντιστοίχιση με το regex.

Ίσως θέλετε να καθορίσετε μια νέα γραμμή (\ n) χαρακτήρα ως μέρος του κειμένου εισαγωγής. Ο μόνος τρόπος για να επιτευχθεί αυτό είναι να καθορίσετε ένα \ χαρακτήρα ακολουθούμενο από ένα ν χαρακτήρας. κύριος() μετατρέπει αυτήν την ακολουθία χαρακτήρων σε τιμή Unicode 10.

Το μεγαλύτερο μέρος του RegexDemoΟ κωδικός βρίσκεται στο δοκιμάστε-σύλληψη κατασκευάσουν. ο δοκιμάστε μπλοκ πρώτα εξάγει το καθορισμένο regex και κείμενο εισαγωγής και στη συνέχεια δημιουργεί ένα Πρότυπο αντικείμενο που αποθηκεύει το μεταγλωττισμένο regex. (Το Regexes συντάσσεται για τη βελτίωση της απόδοσης κατά την αντιστοίχιση μοτίβων.) Ένας αντιστοιχιστής εξάγεται από το Πρότυπο αντικείμενο και χρησιμοποιείται για επανειλημμένη αναζήτηση αγώνων μέχρι να μην παραμείνει κανένα. ο σύλληψη μπλοκ επικαλείται διάφορα PatternSyntaxException μεθόδους για την εξαγωγή χρήσιμων πληροφοριών σχετικά με την εξαίρεση. Αυτές οι πληροφορίες εξάγονται στη συνέχεια.

Δεν χρειάζεται να μάθετε περισσότερα για τη λειτουργία του πηγαίου κώδικα σε αυτό το σημείο. θα γίνει σαφές όταν εξερευνήσετε το API στο Μέρος 2. Ωστόσο, πρέπει να συντάξετε την Καταχώριση 1. Πιάστε τον κωδικό από την Καταχώριση 1 και, στη συνέχεια, πληκτρολογήστε τα ακόλουθα στη γραμμή εντολών σας για μεταγλώττιση RegexDemo:

javac RegexDemo.java

Μοτίβο και οι κατασκευές του

Πρότυπο, η πρώτη από τις τρεις κατηγορίες που περιλαμβάνει το Regex API, είναι μια μεταγλωττισμένη αναπαράσταση μιας κανονικής έκφρασης. ΠρότυποΗ τεκμηρίωση SDK περιγράφει διάφορες δομές regex, αλλά αν δεν είστε ήδη άπληστος χρήστης regex, ενδέχεται να μπερδευτείτε από τμήματα της τεκμηρίωσης. Τι είναι ποσοτικοποιητές και ποια είναι η διαφορά μεταξύ άπληστος, απρόθυμος, και κτητικός ποσοτικοποιητές; Τι είναι τάξεις χαρακτήρων, ταιριαστές ορίων, πίσω αναφορές, και ενσωματωμένες εκφράσεις σημαίας; Θα απαντήσω σε αυτές τις ερωτήσεις και πολλά άλλα στις επόμενες ενότητες.

Κυριολεκτικές χορδές

Η απλούστερη κατασκευή regex είναι η κυριολεκτική συμβολοσειρά. Κάποιο τμήμα του κειμένου εισαγωγής πρέπει να ταιριάζει με το μοτίβο αυτής της δομής για να έχει μια επιτυχημένη αντιστοίχιση μοτίβου. Εξετάστε το ακόλουθο παράδειγμα:

java RegexDemo apple applet

Αυτό το παράδειγμα προσπαθεί να ανακαλύψει εάν υπάρχει αντιστοιχία για το μήλο μοτίβο στο μικροεφαρμογή εισαγωγή κειμένου. Η ακόλουθη έξοδος αποκαλύπτει τον αγώνα:

regex = apple input = applet Βρέθηκε [apple] ξεκινώντας από 0 και τελειώνοντας στις 4

Η έξοδος μας δείχνει το regex και το κείμενο εισαγωγής, και στη συνέχεια δείχνει μια επιτυχημένη αντιστοίχιση του μήλο στα πλαίσια μικροεφαρμογή. Επιπλέον, παρουσιάζει τους δείκτες έναρξης και λήξης αυτού του αγώνα: 0 και 4, αντίστοιχα. Το αρχικό ευρετήριο προσδιορίζει την πρώτη θέση κειμένου όπου συμβαίνει μια αντιστοίχιση μοτίβου. ο τελικός δείκτης προσδιορίζει την τελευταία θέση κειμένου για τον αγώνα.

Τώρα ας υποθέσουμε ότι καθορίζουμε την ακόλουθη γραμμή εντολών:

java RegexDemo apple crabapple

Αυτή τη φορά, έχουμε τον ακόλουθο αγώνα με διαφορετικούς δείκτες έναρξης και λήξης:

regex = apple input = crabapple Βρέθηκε [apple] ξεκινώντας από 4 και τελειώνοντας στις 8

Το αντίστροφο σενάριο, στο οποίο μικροεφαρμογή είναι το regex και μήλο είναι το κείμενο εισαγωγής, δεν αποκαλύπτει αντιστοιχία. Ολόκληρο το regex πρέπει να ταιριάζει, και σε αυτήν την περίπτωση το κείμενο εισαγωγής δεν περιέχει τ μετά μήλο.

Μεταχαρακτήρες

Οι πιο ισχυρές κατασκευές regex συνδυάζουν κυριολεκτικούς χαρακτήρες με μεταχαρακτήρες. Για παράδειγμα, στο α.β, ο μεταχαρακτήρας περιόδου (.) αντιπροσωπεύει οποιονδήποτε χαρακτήρα εμφανίζεται μεταξύ ένα και σι. Εξετάστε το ακόλουθο παράδειγμα:

java RegexDemo .ox "Η γρήγορη καφέ αλεπού πηδά πάνω από το τεμπέλης βόδι."

Αυτό το παράδειγμα καθορίζει .βόδι ως το regex και Η γρήγορη καφέ αλεπού πηδά πάνω από το τεμπέλης βόδι. ως το κείμενο εισαγωγής. RegexDemo αναζητά το κείμενο για αγώνες που ξεκινούν με οποιονδήποτε χαρακτήρα και τελειώνουν με βόδι. Παράγει την ακόλουθη έξοδο:

regex = .ox input = Η γρήγορη καφέ αλεπού πηδά πάνω από το τεμπέλης βόδι. Βρέθηκε [αλεπού] ξεκινώντας στις 16 και τελειώνοντας στις 18 Βρέθηκε [βόδι] ξεκινώντας από 39 και τελειώνοντας με 41

Η έξοδος αποκαλύπτει δύο αγώνες: αλεπού και βόδι (με τον κορυφαίο διαστημικό χαρακτήρα). ο . ο μεταχαρακτήρας ταιριάζει με το φά στον πρώτο αγώνα και τον διαστημικό χαρακτήρα στο δεύτερο αγώνα.

Τι συμβαίνει όταν αντικαθιστούμε .βόδι με το μεταχηματισμό της περιόδου; Δηλαδή, τι αποτέλεσμα προκύπτει από τον καθορισμό της ακόλουθης γραμμής εντολών:

java RegexDemo. "Η γρήγορη καφέ αλεπού πηδά πάνω από το τεμπέλης βόδι."

Επειδή ο μεταχαρακτήρας περιόδου αντιστοιχεί σε οποιονδήποτε χαρακτήρα, RegexDemo εξάγει μια αντιστοίχιση για κάθε χαρακτήρα (συμπεριλαμβανομένου του χαρακτήρα περιόδου λήξης) στο κείμενο εισαγωγής:

regex =. input = Η γρήγορη καφέ αλεπού πηδά πάνω από το τεμπέλης βόδι. Βρέθηκε [T] ξεκινώντας από 0 και τελειώνοντας με 0 Βρέθηκε [h] ξεκινώντας από 1 και τελειώνοντας με 1 Βρέθηκε [e] ξεκινώντας από 2 και τελειώνοντας με 2 Βρέθηκε [] ξεκινώντας από 3 και τελειώνοντας σε 3 Βρέθηκε [q] ξεκινώντας από 4 και τελειώνει στις 4 Βρέθηκαν [u] ξεκινώντας από 5 και τελειώνοντας στις 5 Βρέθηκαν [i] ξεκινώντας από 6 και τελειώνοντας στις 6 Βρέθηκαν [c] ξεκινώντας από 7 και τελειώνοντας στις 7 Βρέθηκαν [k] ξεκινώντας από 8 και τελειώνοντας στις 8 Βρέθηκαν [ ] ξεκινώντας στις 9 και τελειώνοντας στις 9 Βρέθηκαν [b] ξεκινώντας από 10 και τελειώνοντας στις 10 Βρέθηκαν [r] ξεκινώντας από 11 και τελειώνοντας στις 11 Βρέθηκαν [o] ξεκινώντας από 12 και τελειώνοντας στις 12 Βρέθηκαν [w] ξεκινώντας από 13 και τελειώνοντας στις 13 Βρέθηκαν [n] ξεκινώντας από 14 και τελειώνοντας στις 14 Βρέθηκαν [] ξεκινώντας από 15 και τελειώνοντας στις 15 Βρέθηκαν [f] ξεκινώντας από 16 και τελειώνοντας στις 16 Βρέθηκαν [o] ξεκινώντας από 17 και τελειώνοντας στις 17 Βρέθηκαν [x] ξεκινώντας στις 18 και τελειώνει στις 18 Βρέθηκαν [] ξεκινώντας από 19 και τελειώνοντας στις 19 Βρέθηκαν [j] ξεκινώντας από 20 και τελειώνοντας στις 20 Βρέθηκαν [u] ξεκινώντας από 21 και τελειώνοντας στις 21 Βρέθηκαν [m] ξεκινώντας από 22 και τελειώνοντας στις 22 Βρέθηκαν [p] αρχίζει στις 23 και τελειώνει στις 23 Βρέθηκε [s] st arting στα 24 και τελειώνει στις 24 Found [] ξεκινώντας από 25 και τελειώνοντας στις 25 Found [o] ξεκινώντας από 26 και τελειώνοντας στις 26 Found [v] ξεκινώντας από 27 και τελειώνοντας στις 27 Found [e] ξεκινώντας από 28 και τελειώνοντας στις 28 Βρέθηκε [r] ξεκινώντας από 29 και τελειώνοντας με 29 Βρέθηκε [] ξεκινώντας από 30 και τελείωσε στις 30 Βρέθηκε [t] ξεκινώντας από 31 και τελειώνοντας στις 31 Βρέθηκε [h] ξεκινώντας από 32 και τελειώνοντας σε 32 Βρέθηκε [e] ξεκινώντας από 33 και τελειώνει σε 33 Βρέθηκαν [] ξεκινώντας από 34 και τελειώνοντας με 34 Βρέθηκαν [l] ξεκινώντας από 35 και τελειώνοντας με 35 Βρέθηκαν [a] ξεκινώντας από 36 και τελειώνοντας με 36 Βρέθηκαν [z] ξεκινώντας από 37 και τελειώνοντας με 37 Βρέθηκαν [y ] ξεκινώντας από 38 και τελειώνοντας με 38 Βρέθηκαν [] ξεκινώντας από 39 και τελειώνοντας με 39 Βρέθηκαν [o] ξεκινώντας από 40 και τελειώνοντας με 40 Βρέθηκαν [x] ξεκινώντας από 41 και τελειώνοντας με 41 Βρέθηκαν [.] ξεκινώντας από 42 και τελειώνοντας στις 42

Παραθέτοντας μεταχαρακτήρες

Να προσδιορίσω . ή οποιονδήποτε μεταχαρακτήρα ως κυριολεκτικό χαρακτήρα σε ένα regex κατασκεύασμα, παραθέστε τον μεταχαρακτήρα με έναν από τους ακόλουθους τρόπους:

  • Προβάλετε τον μετα-χαρακτήρα με χαρακτήρα ανάστροφης κάθετης.
  • Τοποθετήστε το μεταχαρακτήρα μεταξύ \ Ε και \ΜΙ (π.χ., \ Ε. \ Ε).

Θυμηθείτε να διπλασιάσετε κάθε χαρακτήρα ανάστροφης κάθετης (όπως στο \\. ή \ Ε. \ Ε) που εμφανίζεται σε μια κυριολεκτική συμβολοσειρά όπως Συμβολοσειρά regex = "\.";. Μην διπλασιάζετε τον χαρακτήρα ανάστροφης κάθετης όταν εμφανίζεται ως μέρος ενός ορίσματος γραμμής εντολών.

Κατηγορίες χαρακτήρων

Μερικές φορές πρέπει να περιορίσουμε χαρακτήρες που θα παράγουν αγώνες σε ένα συγκεκριμένο σύνολο χαρακτήρων. Για παράδειγμα, ενδέχεται να αναζητήσουμε κείμενο για φωνήεν ένα, μι, Εγώ, ο, και εσύ, όπου οποιαδήποτε εμφάνιση φωνήεντος δείχνει έναν αγώνα. ΕΝΑ τάξη χαρακτήρων προσδιορίζει ένα σύνολο χαρακτήρων μεταξύ μετα-χαρακτήρων τετραγώνων αγκυλών ([ ], βοηθώντας μας να ολοκληρώσουμε αυτήν την εργασία. Πρότυπο υποστηρίζει απλές τάξεις χαρακτήρων, άρνησης, εύρους, ένωσης, τομής και αφαίρεσης. Θα δούμε όλα αυτά παρακάτω.

Απλή κατηγορία χαρακτήρων

ο απλή κατηγορία χαρακτήρων αποτελείται από χαρακτήρες τοποθετημένους δίπλα-δίπλα και ταιριάζει μόνο με αυτούς τους χαρακτήρες. Για παράδειγμα, [αλφάβητο] ταιριάζει με χαρακτήρες ένα, σι, και ντο.

Εξετάστε το ακόλουθο παράδειγμα:

java RegexDemo [csw] σπηλιά

Αυτό το παράδειγμα ταιριάζει μόνο ντο με το αντίστοιχό του το σπήλαιο, όπως φαίνεται στην ακόλουθη έξοδο:

regex = [csw] input = gua Βρέθηκε [c] ξεκινώντας από 0 και τελειώνοντας στο 0

Κατηγορία χαρακτήρων άρνησης

ο κατηγορία χαρακτήρων άρνησης ξεκινά με το ^ μεταχαρακτήρας και αντιστοιχεί μόνο σε αυτούς τους χαρακτήρες που δεν βρίσκονται σε αυτήν την κλάση. Για παράδειγμα, [^ abc] ταιριάζει με όλους τους χαρακτήρες εκτός ένα, σι, και ντο.

Εξετάστε αυτό το παράδειγμα:

java RegexDemo "[^ csw]" σπηλιά

Σημειώστε ότι τα διπλά εισαγωγικά είναι απαραίτητα στην πλατφόρμα των Windows μου, του οποίου το κέλυφος αντιμετωπίζει το ^ χαρακτήρα ως χαρακτήρα διαφυγής.

Αυτό το παράδειγμα ταιριάζει ένα, β, και μι με τους ομολόγους τους στο σπήλαιο, όπως φαίνεται εδώ:

regex = [^ csw] input = gua Βρέθηκε [a] ξεκινώντας από 1 και τελειώνοντας με 1 Found [v] ξεκινώντας από 2 και τελειώνοντας με 2 Βρέθηκε [e] ξεκινώντας από 3 και τελειώνοντας στις 3

Κατηγορία χαρακτήρων εύρους

ο κατηγορία χαρακτήρων εύρους αποτελείται από δύο χαρακτήρες που διαχωρίζονται από έναν ενωτικό μεταχαρακτήρα (-). Όλοι οι χαρακτήρες που ξεκινούν με τον χαρακτήρα στα αριστερά της παύλας και τελειώνουν με τον χαρακτήρα στα δεξιά της παύλας ανήκουν στην περιοχή. Για παράδειγμα, [α-ζ] ταιριάζει με όλους τους πεζούς αλφαβητικούς χαρακτήρες. Είναι ισοδύναμο με τον καθορισμό [abcdefghijklmnopqrstuvwxyz].

Εξετάστε το ακόλουθο παράδειγμα:

java RegexDemo [a-c] κλόουν

Αυτό το παράδειγμα ταιριάζει μόνο ντο με το αντίστοιχό του το κλόουν, όπως φαίνεται:

regex = [a-c] input = clown Βρέθηκε [c] ξεκινώντας από 0 και τελειώνοντας στο 0

Συγχώνευση πολλαπλών περιοχών

Μπορείτε να συγχωνεύσετε πολλές περιοχές στην ίδια κατηγορία χαρακτήρων εύρους τοποθετώντας τις δίπλα-δίπλα. Για παράδειγμα, [α-ζΑ-Ζ] ταιριάζει με όλους τους πεζούς και κεφαλαίους αλφαβητικούς χαρακτήρες.

Κατηγορία χαρακτήρα Ένωσης

ο τάξη χαρακτήρα συνδικάτου αποτελείται από πολλές κατηγορίες ένθετων χαρακτήρων και ταιριάζει με όλους τους χαρακτήρες που ανήκουν στην ένωση που προκύπτει. Για παράδειγμα, [α-δ [m-p]] ταιριάζει με χαρακτήρες ένα διά μέσου ρε και Μ διά μέσου Π.

Εξετάστε το ακόλουθο παράδειγμα:

java RegexDemo [ab [c-e]] abcdef

Αυτό το παράδειγμα ταιριάζει ένα, σι, ντο, ρε, και μι με τους ομολόγους τους στο abcdef:

regex = [ab [ce]] input = abcdef Βρέθηκε [a] ξεκινώντας από το 0 και τελειώνοντας στο 0 Βρέθηκε [b] ξεκινώντας από 1 και τελειώνοντας στο 1 Βρέθηκε [c] ξεκινώντας από 2 και τελειώνοντας στις 2 Βρέθηκε [d] ξεκινώντας από 3 και τελειώνει στις 3 Βρέθηκε [e] ξεκινώντας από 4 και τελειώνοντας στις 4

Κατηγορία χαρακτήρων διατομής

ο κλάση χαρακτήρων διασταύρωσης αποτελείται από χαρακτήρες κοινούς σε όλες τις ένθετες τάξεις και ταιριάζει μόνο με κοινούς χαρακτήρες. Για παράδειγμα, [a-z && [d-f]] ταιριάζει με χαρακτήρες ρε, μι, και φά.

Εξετάστε το ακόλουθο παράδειγμα:

java RegexDemo "[aeiouy && [y]]" πάρτι

Σημειώστε ότι τα διπλά εισαγωγικά είναι απαραίτητα στην πλατφόρμα των Windows μου, του οποίου το κέλυφος αντιμετωπίζει το & χαρακτήρα ως διαχωριστής εντολών.

Αυτό το παράδειγμα ταιριάζει μόνο γ με το αντίστοιχό του το κόμμα:

regex = [aeiouy && [y]] input = party Βρέθηκε [y] ξεκινώντας από 4 και τελειώνοντας στις 4