Un Regex che non potrà mai eguagliare nulla


131

Potrebbe sembrare una domanda stupida, ma ho avuto una lunga conversazione con alcuni dei miei colleghi sviluppatori e sembrava una cosa divertente a cui pensare.

Così; qual è il tuo pensiero: che aspetto ha un Regex, che non sarà mai eguagliato da nessuna stringa!

Modifica : Perché lo voglio? Bene, in primo luogo perché trovo interessante pensare a una tale espressione e in secondo luogo perché ne ho bisogno per una sceneggiatura.

In quello script definisco un dizionario come Dictionary<string, Regex>. Questo contiene, come vedi, una stringa e un'espressione.

Sulla base di quel dizionario creo metodi che usano tutti questo dizionario solo come riferimento su come dovrebbero fare il loro lavoro, uno di questi corrisponde alle regex con un file di log analizzato.

Se un'espressione corrisponde, un'altra Dictionary<string, long> viene aggiunto un valore che viene restituito dall'espressione. Quindi, per catturare tutti i messaggi di log che non corrispondono a un'espressione nel dizionario, ho creato un nuovo gruppo chiamato "sconosciuto".

A questo gruppo viene aggiunto tutto ciò che non corrisponde ad altro. Ma per evitare che l'espressione "sconosciuta" non corrispondesse (per caso) a un messaggio di registro, ho dovuto creare un'espressione che sicuramente non ha mai eguagliato, indipendentemente dalla stringa che gli ho dato.

Quindi, lì hai la mia ragione per questa "non una vera domanda" ...


1
Si noti che è molto difficile dimostrare un aspetto negativo.
Lasse V. Karlsen,

5
Interessante. Dove useresti una regex del genere?
Charlie Salts,

1
Noterò qui per la cronaca che molti dei commenti sopra, e le risposte a questa domanda, erano originariamente da stackoverflow.com/questions/1845078/… che è quello che ho chiesto. Marc Gravell li ha uniti, il che a mio avviso rende molte di queste risposte un po 'bizzarre senza il preciso contesto originale, al punto che alcuni commenti non sembrano avere senso. (Probabilmente ruba anche potenziali punti di replicazione futuri.) Suggerirei che domande con background così dettagliati non possano mai essere "duplicati esatti". Qualunque cosa ...
Peter Hansen,

2
Questa domanda è stata aggiunta alle FAQ sulle espressioni regolari di overflow dello stack , in "Advanced Regex-Fu".
aliteralmind,

3
"Nota che è molto difficile dimostrare un negativo" - questo è ampiamente creduto ma assolutamente e ovviamente falso ... come abbiamo saputo almeno da quando Euclide ha dimostrato che non esiste un numero primo. E qualsiasi prova di P è una prova della negazione di (non di P). Ciò che è vero è che è difficile dimostrare un universale empirico, positivo o negativo, ad esempio "tutti i corvi sono neri" o "nessun corvo è bianco". Gli algoritmi sono analitici, non empirici, quindi questa è una cattiva applicazione della regola fasulla. ad esempio, una prova che il modello 'a' non corrisponde a nessuna stringa che inizia con 'b' non è "molto difficile".
Jim Balter,

Risposte:


66

Questo è in realtà abbastanza semplice, anche se dipende dall'implementazione / flag *:

$a

Abbinerà un carattere adopo la fine della stringa. In bocca al lupo.

ATTENZIONE:
questa espressione è costosa: scansionerà l'intera linea, troverà l'ancoraggio di fine linea e solo allora non troverà ae restituirà una corrispondenza negativa. (Vedi commento sotto per maggiori dettagli.)


* Inizialmente non avevo pensato molto al regexp in modalità multilinea, dove $corrisponde anche alla fine di una riga. In effetti, corrisponderebbe alla stringa vuota proprio prima della nuova riga , quindi un carattere ordinario come anon potrà mai apparire dopo $.


50
Questa espressione è costosa: scansionerà l'intera linea, troverà l'ancoraggio di fine linea e solo allora non troverà la "a" e restituirà una corrispondenza negativa. Vedo che ci vogliono ~ 480ms per scansionare un file di linea ~ 275k. Il contrario "a ^" richiede circa lo stesso tempo, anche se potrebbe sembrare più efficiente. D'altra parte, uno sguardo negativo non deve necessariamente scansionare nulla: "(?! X) x" (qualsiasi cosa non seguita da una x seguita anche da una x, cioè nulla) richiede circa 30 ms, o meno del 7% delle volte. (Misurato con gnu time ed egrep.)
arantius

1
In Perl che corrisponderà al valore corrente di $a. Anche l'equivalente Perl $(?:a)è molto lento perl -Mre=debug -e'$_=a x 50; /$(?:a)/'.
Brad Gilbert,

@arantius, per favore vedi la mia risposta riguardo ai tempi , poiché ho trovato l'esatto contrario misurato con timeite python3.
nivk,

Non è scioccante che sei anni e una versione principale di Python possano cambiare le cose.
Arantius,

1
Nella sintassi POSIX BRE, $acorrisponderà al testo letterale $a, perché $non è valido come ancoraggio in quel modello.
phils,

76

Leva negative lookahead:

>>> import re
>>> x=r'(?!x)x'
>>> r=re.compile(x)
>>> r.match('')
>>> r.match('x')
>>> r.match('y')

questa RE è una contraddizione in termini e quindi non corrisponderà mai a nulla.

NOTA:
In Python, re.match () aggiunge implicitamente un punto di ancoraggio iniziale ( \A) all'inizio dell'espressione regolare. Questo ancoraggio è importante per le prestazioni: senza di esso, l'intera stringa verrà scansionata. Coloro che non usano Python vorranno aggiungere l'ancoraggio in modo esplicito:

\A(?!x)x

@Chris, yep - also, (?=x)(?!x)e così via (concatenazioni di punti di vista contraddittori, e lo stesso vale per i lookbehinds), e molti di questi funzionano anche per valori arbitrari di x(i lookbehinds hanno bisogno di xs che corrispondano a stringhe di lunghezza fissa).
Alex Martelli,

1
Sembra funzionare bene. Ma che dire di (?!) Invece? Dato che () corrisponderà sempre, (?!) Non sarebbe mai garantito che corrispondano?
Peter Hansen,

2
@Peter, sì, se Python accetta quella sintassi (e le versioni recenti sembrano), allora sarebbe anche contraddittorio. Un'altra idea (non altrettanto elegante, ma più idee ottieni, più è probabile che tu ne trovi una che funzioni su tutti i motori RE di interesse): r'a\bc'cercare un confine di parole immediatamente circondato da lettere su entrambi i lati (variante: caratteri non parole su entrambe le parti).
Alex Martelli,

1
È interessante notare che il mio originale con un semplice letterale che "conosco" non apparirà nei miei input risulta essere il più veloce, in Python. Con una stringa di input di 5 MB e utilizzando questa operazione in un'operazione sub (), (?! X) x richiede il 21% in più, (?! ()) È del 16% e ($ ^) il 6% in più. Può essere significativo in alcuni casi, anche se non nel mio.
Peter Hansen,

2
Può essere piuttosto lento perl -Mre=debug -e'$_=x x 8; /(?!x)x/'. Puoi renderlo più veloce ancorandolo all'inizio \A(?!x)xo alla fine (?!x)x\z. perl -Mre=debug -e'$_=x x 8; /(?!x)x\z/; /\A(?!x)x/'
Brad Gilbert,

43

Uno che è mancato:

^\b$

Non può corrispondere perché la stringa vuota non contiene un limite di parole. Testato in Python 2.5.


7
Questa è la risposta migliore Non utilizza lookaheads, non si interrompe in alcune implementazioni di regex, non utilizza un carattere specifico (ad esempio 'a') e non riesce in un massimo di 3 passaggi di elaborazione (secondo regex101.com) senza eseguire la scansione dell'intero stringa di input. Questo è anche facile da capire a colpo d'occhio.
CubicleSoft

1
Questo in realtà fallisce in Emacs in determinate condizioni (se c'è una riga vuota all'inizio o alla fine del buffer), tuttavia \`\b\'funziona, il che sostituisce la sintassi di Emacs con "inizio / fine del testo" (al contrario di "inizio / fine" di linea ").
phils,

35

guardati intorno:

(?=a)b

Per i principianti regex: lo sguardo positivo in avanti (?=a)assicura che il carattere successivo sia a, ma non cambia la posizione di ricerca (o include la 'a' nella stringa corrispondente). Ora che il personaggio successivo è confermato a, la parte rimanente di regex ( b) corrisponde solo se lo è il personaggio successivo b. Pertanto, questa regex corrisponde solo se un personaggio è entrambi ae ballo stesso tempo.


30

a\bc, dove \bè un'espressione di larghezza zero che corrisponde al limite di parole.

Non può apparire nel mezzo di una parola, a cui la forziamo.


Se il tuo caso d'uso ti consente di ancorare il pattern all'inizio della stringa, quel miglioramento impedirà al motore regexp di cercare e testare ogni istanza di un anel testo.
phils,

20

$.

.^

$.^

(?!)


1
Carina! Il mio subconscio mi ha allontanato da idee come le prime tre, poiché sono "illegali" ... concettualmente, ma ovviamente non alla regex. Non riconosco quello (!) ... dovrò cercarlo.
Peter Hansen,

1
Va bene allora, mi piace la risposta (?!) ... efficacemente ciò che Alex ha suggerito. Si noti che in stackoverflow.com/questions/1723182 (sottolineato da Amarghosh sopra) qualcuno afferma che "alcuni sapori" di regex considererebbero un errore di sintassi. Python piace comunque. Nota che tutti gli altri tuoi suggerimenti fallirebbero con le modalità re.DOTALL | re.MULTILINE in Python.
Peter Hansen,

1
Questo è stato testato? Avrei ipotizzato che ^abbia solo un significato speciale come primo carattere di una regexp, e $abbia un significato speciale solo alla fine di una regexp, a meno che l'espressione regolare non sia un'espressione a più righe.
PP.

In realtà in Perl /$./significa qualcosa di completamente diverso. Significa che corrisponde al valore corrente di $.(numero riga di input) . Anche /$(.)/potrebbe abbinare qualcosa se hai scritto use re '/s';prima. ( perl -E'say "\n" =~ /$(.)/s || 0')
Brad Gilbert,

Nella sintassi POSIX BRE, ^e $sono speciali solo all'inizio e alla fine (rispettivamente) del modello, quindi nessuno $.o .^o $.^funzionerebbe. (?!)è una funzionalità Perl / PCRE, credo.
phils,

13

Corrispondenza massima

a++a

Almeno uno aseguito da un numero qualsiasi di a, senza backtracking. Quindi prova a abbinarne uno in più a.

o sottoespressione indipendente

Ciò equivale a inserire a+un'espressione secondaria indipendente, seguita da un'altra a.

(?>a+)a

10

Perl 5.10 supporta speciali parole di controllo chiamate "verbi", che è racchiuso in (*...)sequenza. (Confronta con (?...)una sequenza speciale.) Tra questi, include il (*FAIL)verbo che ritorna immediatamente dall'espressione regolare.

Nota che anche i verbi sono implementati in PCRE poco dopo, quindi puoi usarli in PHP o in altre lingue usando anche la libreria PCRE. (Non è possibile in Python o Ruby, tuttavia. Usano il proprio motore.)


I documenti per quello su perldoc.perl.org/perlre.html#%28%2AFAIL%29-%28%2AF%29 dicono "Questo modello non corrisponde a nulla e fallisce sempre. È equivalente a (?!), Ma più facile da leggi. In effetti, (?!) viene ottimizzato in (* FAIL) internamente. " Interessante, dato che (?!) È la mia risposta "pura" preferita finora (anche se non funziona in Javascript). Grazie.
Peter Hansen,

10
\B\b

\bcorrisponde ai limiti delle parole: la posizione tra una lettera e una non lettera (o il limite della stringa).
\Bè il suo complemento: corrisponde alla posizione tra due lettere o tra non lettere.

Insieme non possono eguagliare alcuna posizione.

Guarda anche:


Questa sembra un'ottima soluzione, a condizione che sia ancorata a un punto specifico (l'inizio del testo sembrerebbe ragionevole). Se non lo fai, allora è una soluzione terribile , perché ogni limite non di parole nel testo verrà testato per vedere se è seguito da un limite di parole! Quindi la versione ragionevole sarebbe qualcosa del genere ^\B\b. Nelle lingue in cui "inizio del testo" e "inizio della riga" hanno una sintassi diversa, ti consigliamo di utilizzare la sintassi "inizio del testo", altrimenti testerai ogni riga. (ad esempio in Emacs questo sarebbe \`\B\bo "\\`\\B\\b".)
phils

Detto questo, ho notato che lo scopo dichiarato di questa domanda è ottenere una regexp da usare in un gruppo, nel qual caso ^è problematica in una certa sintassi regexp (es. POSIX BRE) dove ^è solo un'ancora quando è il primo carattere del modello, e altrimenti corrisponde a un ^carattere letterale .
phils,

@phils - Penso che tu lo stia pensando troppo :)- questa è una domanda non pratica, in cui l'obiettivo era trovare una risposta interessante, non una risposta efficiente. Detto questo, il pattern può essere rifiutato in tempo di rivestimento (con le dimensioni della stringa target), quindi non è male per una regex - la maggior parte dei pattern qui sono gli stessi e ^potrebbe anche essere lineare se non è ottimizzato.
Kobi,

Ri: ottimizzazioni, sono disposto a ignorare un motore regexp che spera di trovare "l'inizio del testo" in qualsiasi altra posizione :)
phils,

Inoltre, non è un D&R così poco pratico - l'unica ragione per cui sono finito qui è stato vedere se qualcuno potesse suggerire una mia soluzione più efficiente per il mio scopo pratico di configurare una particolare variabile Emacs che richiedeva un valore regexp, ma che io voleva disabilitare efficacemente.
phils,

8

Questo sembra funzionare:

$.

2
È simile all'esempio di Ferdinand Beyer.
Gumbo,

9
E corrisponderà in modalità punto-partite-newline.
Tim Pietzcker,

In Perl che corrisponderà effettivamente con il numero della riga di input corrente $.. In tal caso devi ricorrere a $(.)o in modo più equivalente $(?:.).
Brad Gilbert,

Nella sintassi POSIX BRE, $.corrisponderà a un valore letterale $seguito da qualsiasi carattere, poiché $non è valido come ancoraggio in quel modello.
phils,

8

Che ne dici $^o forse (?!)


3
Un'interruzione di riga verrà abbinata a questa espressione nella modalità in cui ^corrisponde all'inizio e $alla fine di una riga.
Gumbo,

4
Forse intendeva (?!): uno sguardo negativo per una stringa vuota. Ma alcuni sapori regex lo considereranno anche un errore di sintassi.
Alan Moore

1
Una stringa vuota corrisponde alla prima, almeno in JavaScript.
Roland Pihlakas,

Nella sintassi POSIX BRE, $^corrisponderà a quei caratteri letterali, perché i caratteri non sono validi come ancore (cioè il motivo stesso per cui hai usato il modello fa sì che non faccia quello che volevi.)
phils

5

Il più veloce sarà:

r = re.compile(r'a^')
r.match('whatever')

'a' può essere qualsiasi carattere non speciale ('x', 'y'). L'implementazione di Knio potrebbe essere un po 'più pura, ma questa sarà più veloce per tutte le stringhe che non iniziano con qualsiasi carattere tu scelga invece di' a 'perché non corrisponderà dopo il primo carattere piuttosto che dopo il secondo in quei casi.


In effetti, (. ^) Nel mio caso sarebbe circa il 10% più lento di (\ x00 ^).
Peter Hansen,

1
Lo accetto, dal momento che l'utilizzo di qualsiasi valore diverso da \ n poiché il personaggio è garantito non corrisponderà mai e lo vedo come leggermente più leggibile (dato che relativamente poche persone sono esperti di regex) rispetto all'opzione (?! X) x , anche se ho votato anche quello. Nel mio caso, per entrambe le opzioni avrei bisogno di un commento per spiegarlo, quindi penso che regolerò il mio tentativo originale su '\ x00NEVERMATCHES ^'. Ottengo la garanzia senza eguali di questa risposta, con la mia originale auto-documentazione. Grazie a tutti per le risposte!
Peter Hansen,

3
Funziona davvero e, in tal caso, chi ha deciso di rompere con Unix? In Unix regexps, ^è speciale solo come primo personaggio e similmente con $. Con qualsiasi strumento Unix, quel regexp corrisponderà a qualsiasi cosa contenga la stringa letterale a^.
JaakkoK,

Heh, è ​​un buon attacco. Non ho mai provato contro quella stringa letterale.
Adam Nelson,

Oh, se questo rompe le regexps di Unix, allora ti piacerà >^.
CubicleSoft

4

Python non lo accetterà, ma Perl:

perl -ne 'print if /(w\1w)/'

Questa regex dovrebbe (teoricamente) cercare di far corrispondere un numero infinito (pari) di ws, perché il primo gruppo ( ()i) ricorre in se stesso. Sembra che Perl non stia emettendo alcun avvertimento, nemmeno sotto use strict; use warnings;, quindi presumo che sia almeno valido e i miei test (minimi) non riescano a eguagliare nulla, quindi lo sottopongo alla tua critica.


1
La teoria è sempre piacevole, ma in pratica penso che sarei preoccupato per le espressioni regolari le cui descrizioni includevano la parola "infinito"!
Peter Hansen,

perl -Mre=debug -e'"www wwww wwwww wwwwww" =~ /(w\1w)/'
Brad Gilbert,

@BradGilbert - L'esecuzione qui (5.10, un po 'obsoleta) produce "regex fallito", come richiesto dall'OP. Corrisponde al tuo sistema?
Chris Lutz,

4

[^\d\D]oppure (?=a)boppure a$aoppurea^a


Grazie. Si noti che (?! X) x è stata la prima risposta fornita, elencata sopra.
Peter Hansen,

Sì, mi è sembrato di scansionare gli altri risponditori troppo rapidamente.
Bart Kiers,

4

Questo non funzionerà per Python e molte altre lingue, ma in una regex Javascript []è una classe di caratteri valida che non può essere abbinata. Quindi, quanto segue dovrebbe fallire immediatamente, indipendentemente dall'input:

var noMatch = /^[]/;

Mi piace meglio che /$a/perché per me comunica chiaramente il suo intento. E quando ne avresti mai avuto bisogno, ne avevo bisogno perché avevo bisogno di un fallback per un modello compilato dinamicamente in base all'input dell'utente. Quando il modello non è valido, devo sostituirlo con un modello che non corrisponde a nulla. Semplificato, si presenta così:

try {
    var matchPattern = new RegExp(someUserInput);
}
catch (e) {
    matchPattern = noMatch;
}

4

Tutti gli esempi che coinvolgono un matcher di confine seguono la stessa ricetta. Ricetta:

  1. Prendi uno dei corrispondenti al contorno: ^, $, \ b, \ A, \ Z, \ z

  2. Fai l'opposto di ciò per cui sono destinati

Esempi:

^ e \ A sono pensati per l'inizio, quindi non usarli all'inizio

^ --> .^
\A --> .\A

\ b corrisponde a un limite di parole, quindi usalo tra

\b --> .\b.

$, \ Z e \ z sono pensati per la fine, quindi non usarli alla fine

$ --> $.
\Z --> \Z.
\z --> \z.

Altri comportano l'uso di lookahead e lookbehind che funzionano anche con la stessa analogia: se dai lookahead positivo o negativo seguito da qualcosa di opposto

(?=x)[^x]
(?!x)x

Se dai un'occhiata positiva o negativa seguendo qualcosa di opposto

[^x](?<=x)
x(?<!x)

Il loro potrebbe essere più tale schema e più simili analogie.


3

Tante buone risposte!

Simile alla risposta di @nivk, vorrei condividere il confronto delle prestazioni per Perl per diverse varianti di regex senza eguali.

  1. Input: stringhe ASCII pseudo-casuali (25.000 righe diverse, lunghezza 8-16):

Velocità Regex:

Total for   \A(?!x)x: 69.675450 s, 1435225 lines/s
Total for       a\bc: 71.164469 s, 1405195 lines/s
Total for    (?>a+)a: 71.218324 s, 1404133 lines/s
Total for       a++a: 71.331362 s, 1401907 lines/s
Total for         $a: 72.567302 s, 1378031 lines/s
Total for     (?=a)b: 72.842308 s, 1372828 lines/s
Total for     (?!x)x: 72.948911 s, 1370822 lines/s
Total for       ^\b$: 79.417197 s, 1259173 lines/s
Total for         $.: 88.727839 s, 1127041 lines/s
Total for       (?!): 111.272815 s, 898692 lines/s
Total for         .^: 115.298849 s, 867311 lines/s
Total for    (*FAIL): 350.409864 s, 285380 lines/s
  1. Input: / usr / share / dict / words (100.000 parole inglesi).

Velocità Regex:

Total for   \A(?!x)x: 128.336729 s, 1564805 lines/s
Total for     (?!x)x: 132.138544 s, 1519783 lines/s
Total for       a++a: 133.144501 s, 1508301 lines/s
Total for    (?>a+)a: 133.394062 s, 1505479 lines/s
Total for       a\bc: 134.643127 s, 1491513 lines/s
Total for     (?=a)b: 137.877110 s, 1456528 lines/s
Total for         $a: 152.215523 s, 1319326 lines/s
Total for       ^\b$: 153.727954 s, 1306346 lines/s
Total for         $.: 170.780654 s, 1175906 lines/s
Total for       (?!): 209.800379 s, 957205 lines/s
Total for         .^: 217.943800 s, 921439 lines/s
Total for    (*FAIL): 661.598302 s, 303540 lines/s

(Ubuntu su Intel i5-3320M, kernel Linux 4.13, Perl 5.26)


Ecco un confronto JavaScript di alcuni metodi trattati qui: jsperf.com/regex-that-never-matches
thdoan

2

credo che

\Z RE FAILS! \A

copre anche i casi in cui l'espressione regolare include flag come MULTILINE, DOTALL ecc.

>>> import re
>>> x=re.compile(r"\Z RE FAILS! \A")
>>> x.match('')
>>> x.match(' RE FAILS! ')
>>>

Credo (ma non l'ho confrontato) che qualunque sia la lunghezza (> 0) della stringa tra \Ze \A, il tempo di fallimento dovrebbe essere costante.


2
(*FAIL)

o

(*F)

Con PCRE e PERL è possibile utilizzare questo verbo di controllo di backtracking che forza il pattern a fallire immediatamente.


2

Dopo aver visto alcune di queste grandi risposte, il commento di @ arantius (per quanto riguarda tempismo $xvs x^vs(?!x)x ) sulla risposta attualmente accettata mi ha fatto venire voglia di valutare alcune delle soluzioni fornite finora.

Usando lo standard di linea 275k di @ arantius, ho eseguito i seguenti test in Python (v3.5.2, IPython 6.2.1).

TL; DR: 'x^'e 'x\by'sono i più veloci di almeno un fattore di ~ 16, e contrariamente a quanto scoperto da @ arantius, è (?!x)xstato tra i più lenti (~ 37 volte più lento). Quindi la domanda sulla velocità dipende sicuramente dall'implementazione. Provalo tu stesso sul tuo sistema previsto prima di impegnarti se la velocità è importante per te.

AGGIORNAMENTO: sembra esserci una grande discrepanza tra i tempi 'x^'e 'a^'. Si prega di consultare questa domanda per ulteriori informazioni e la modifica precedente per i tempi più lenti con ainvece di x.

In [1]: import re

In [2]: with open('/tmp/longfile.txt') as f:
   ...:     longfile = f.read()
   ...:     

In [3]: len(re.findall('\n',longfile))
Out[3]: 275000

In [4]: len(longfile)
Out[4]: 24733175

In [5]: for regex in ('x^','.^','$x','$.','$x^','$.^','$^','(?!x)x','(?!)','(?=x)y','(?=x)(?!x)',r'x\by',r'x\bx',r'^\b$'
    ...: ,r'\B\b',r'\ZNEVERMATCH\A',r'\Z\A'):
    ...:     print('-'*72)
    ...:     print(regex)
    ...:     %timeit re.search(regex,longfile)
    ...:     
------------------------------------------------------------------------
x^
6.98 ms ± 58.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
------------------------------------------------------------------------
.^
155 ms ± 960 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
$x
111 ms ± 2.12 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
$.
111 ms ± 1.76 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
$x^
112 ms ± 1.14 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
$.^
113 ms ± 1.44 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
$^
111 ms ± 839 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
(?!x)x
257 ms ± 5.03 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
------------------------------------------------------------------------
(?!)
203 ms ± 1.56 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
(?=x)y
204 ms ± 4.84 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
------------------------------------------------------------------------
(?=x)(?!x)
210 ms ± 1.66 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
------------------------------------------------------------------------
x\by
7.41 ms ± 122 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
------------------------------------------------------------------------
x\bx
7.42 ms ± 110 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
------------------------------------------------------------------------
^\b$
108 ms ± 1.05 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
\B\b
387 ms ± 5.77 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
------------------------------------------------------------------------
\ZNEVERMATCH\A
112 ms ± 1.52 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
\Z\A
112 ms ± 1.38 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

La prima volta che ho eseguito questo, mi sono dimenticato di raw delle ultime 3 espressioni, quindi è '\b'stato interpretato come '\x08'il personaggio backspace. Tuttavia, con mia sorpresa, è 'a\x08c'stato più veloce del precedente risultato più veloce! Ad essere sinceri, corrisponderà comunque a quel testo, ma ho pensato che valesse la pena notare perché non sono sicuro del perché sia ​​più veloce.

In [6]: for regex in ('x\by','x\bx','^\b$','\B\b'):
    ...:     print('-'*72)
    ...:     print(regex, repr(regex))
    ...:     %timeit re.search(regex,longfile)
    ...:     print(re.search(regex,longfile))
    ...:     
------------------------------------------------------------------------
y 'x\x08y'
5.32 ms ± 46.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
None
------------------------------------------------------------------------
x 'x\x08x'
5.34 ms ± 66.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
None
------------------------------------------------------------------------
$ '^\x08$'
122 ms ± 1.05 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
None
------------------------------------------------------------------------
\ '\\B\x08'
300 ms ± 4.11 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
None

Il mio file di test è stato creato usando una formula per "... Contenuti leggibili e nessuna linea duplicata" (su Ubuntu 16.04):

$ ruby -e 'a=STDIN.readlines;275000.times do;b=[];rand(20).times do; b << a[rand(a.size)].chomp end; puts b.join(" "); end' < /usr/share/dict/words > /tmp/longfile.txt

$ head -n5 /tmp/longfile.txt 
unavailable speedometer's garbling Zambia subcontracted fullbacks Belmont mantra's
pizzicatos carotids bitch Hernandez renovate leopard Knuth coarsen
Ramada flu occupies drippings peaces siroccos Bartók upside twiggier configurable perpetuates tapering pint paralyzed
vibraphone stoppered weirdest dispute clergy's getup perusal fork
nighties resurgence chafe

\B\bè orribilmente imperfetto dal punto di vista delle prestazioni (come ogni schema che non è ancorato ad una posizione, ma questo schema è particolarmente negativo). Prova ^\B\binvece il benchmarking .
phils,

2

Regex vuoto

La regex migliore per non abbinare mai nulla è una regex vuota. Ma non sono sicuro che tutto il motore regex lo accetti.

Regex impossibile

L'altra soluzione è creare una regex impossibile. Ho scoperto che $-^bastano due passaggi per calcolare indipendentemente dalla dimensione del testo ( https://regex101.com/r/yjcs1Z/1 ).

Per riferimento:

  • $^e $.fai 36 passi per calcolare -> O (1)
  • \b\B compie 1507 passi sul mio campione e aumenta con il numero di caratteri nella stringa -> O (n)

Discussione più popolare su questa domanda:


1

Forse questo?

/$.+^/

In Python, questo approccio funziona solo se controlli i flag : re.compile('$.+^', re.MULTILINE|re.DOTALL).search('a\nb\nc\n')restituisce un oggetto match corrispondente a b e c (e tutte le newline adiacenti e intermedie). L'approccio con aspetto negativo che raccomando funziona (cioè non riesce a eguagliare nulla) per qualsiasi combinazione di flag con cui potrebbe essere compilato.
Alex Martelli,

Mio cattivo - confuso il $e ^.
Chris Lutz,

1
Questo potrebbe essere un tentativo di cercare la fine di una stringa prima dell'inizio, ma ho scoperto che $ non significa "fine della stringa" a meno che non sia l'ultimo carattere della regex e mi aspetto che si applichi un comportamento simile a ^, quindi questo potrebbe corrispondere a una sottostringa che inizia con un letterale $ e termina con un letterale ^
pavium

@pavium, certamente non si comporta così in Python o Javascript. A meno che tu non li sfugga con \ o li includa in un set di caratteri con [], i caratteri speciali come $ e ^ non dovrebbero essere trattati come letterali. In che lingua l'hai osservato?
Peter Hansen,

Almeno in Perl dovrebbe essere scritto /\z.+\A/(vedi perldoc perlre ) che impedisce alla modalità multi-riga e single-line ( use re '/ms') di influenzarla.
Brad Gilbert,

0
'[^0-9a-zA-Z...]*'

e sostituisci ... con tutti i simboli stampabili;). Questo è per un file di testo.


Penso che ci debba essere un modo più breve per farlo, ma anche quello è stato il mio primo pensiero ^^
FP

4
Questo corrisponderà alla stringa vuota. Per catturare ogni possibile carattere, utilizzare [^\x00-\xFF]+(per implementazioni basate su byte).
Ferdinand Beyer,

6
Un'espressione migliore sarebbe [^\s\S]. Ma come già detto da Ferdinand Beyer, corrisponderebbe a una stringa vuota.
Gumbo,

3
La regex di Drakosha può abbinare una stringa vuota a causa del *; lascialo spento o sostituiscilo con +, e deve corrispondere ad almeno un personaggio. Se la classe esclude tutti i possibili caratteri, non può eguagliare nulla.
Alan Moore,

0

Che dire invece di regex, basta usare un'istruzione if sempre falsa? In javascript:

var willAlwaysFalse=false;
if(willAlwaysFalse)
{
}
else
{
}

Ho aggiunto un commento in risposta alla domanda di Charlie, spiegando perché questo tipo di approccio non è desiderabile. In breve, ho bisogno di un gruppo all'interno di una regex che sarà sempre usato, ma in alcuni casi il gruppo deve essere creato per assicurarsi che non possa mai eguagliare.
Peter Hansen,

-2

Una soluzione portatile che non dipenderà dall'implementazione di regexp è semplicemente usare una stringa costante che si è certi che non comparirà mai nei messaggi di registro. Ad esempio, crea una stringa in base a quanto segue:

cat /dev/urandom | hexdump | head -20
0000000 5d5d 3607 40d8 d7ab ce72 aae1 4eb3 ae47
0000010 c5e2 b9e8 910d a2d9 2eb3 fdff 6301 c85f
0000020 35d4 c282 e439 33d8 1c73 ca78 1e4d a569
0000030 8aca eb3c cbe4 aff7 d079 ca38 8831 15a5
0000040 818b 323f 0b02 caec f17f 387b 3995 88da
0000050 7b02 c80b 2d42 8087 9758 f56f b71f 0053
0000060 1501 35c9 0965 2c6e 03fe 7c6d f0ca e547
0000070 aba0 d5b6 c1d9 9bb2 fcd1 5ec7 ee9d 9963
0000080 6f0a 2c91 39c2 3587 c060 faa7 4ea4 1efd
0000090 6738 1a4c 3037 ed28 f62f 20fa 3d57 3cc0
00000a0 34f0 4bc2 3067 a1f7 9a87 086b 2876 1072
00000b0 d9e1 6b8f 5432 a60e f0f5 00b5 d9ef ed6f
00000c0 4a85 70ee 5ec4 a378 7786 927f f126 2ec2
00000d0 18c5 46fe b167 1ae6 c87c 1497 48c9 3c09
00000e0 8d09 e945 13ce 7da2 08af 1a96 c24c c022
00000f0 b051 98b3 2bf5 4d7d 5ec4 e016 a50d 355b
0000100 0e89 d9dd b153 9f0e 9a42 a51f 2d46 2435
0000110 ef35 17c2 d2aa 3cc7 e2c3 e711 d229 f108
0000120 324e 5d6a 650a d151 bc55 963f 41d3 66ee
0000130 1d8c 1fb1 1137 29b2 abf7 3af7 51fe 3cf4

Certo, questa non è una sfida intellettuale, ma più simile alla programmazione del nastro isolante .


-6
new Regex(Guid.NewGuid().ToString())

Crea un modello contenente solo caratteri alfanumerici e ' -' (nessuno dei quali sono caratteri speciali regex) ma è statisticamente impossibile che la stessa stringa sia apparsa ovunque prima (perché questo è l'intero punto di un GUID.)


2
"Statisticamente impossibile"? Eh? A seconda di come viene calcolato il GUID, è possibile e spesso abbastanza semplice prevedere i GUID successivi (poiché dipendono dal computer che li calcola e dal tempo). Intendi "improbabile", "con una probabilità molto piccola", ma non puoi dire "impossibile" anche per stringhe perfettamente casuali. Il tuo Regex corrisponderà a un numero infinito di stringhe: questa domanda ne sta cercando una che non corrisponderà a nulla. Mai.
Ferdinand Beyer
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.