2015-07-27 14 views
6

Utilizzando std :: regex e dato un percorso di file, voglio abbinare solo i nomi di file che terminano con .txt e che non sono del modulo _test.txt o .txtTEMP. Qualsiasi altro carattere di sottolineatura va bene.Regex per abbinare solo alcuni nomi di file

Così, ad esempio:

  • somepath/testFile.txt deve corrispondere.
  • somepath/test_File.txt deve corrispondere.
  • somepath/testFile_test.txt non corrisponde.
  • somepath/testFile.txtTEMP non corrisponde.

Qual è il regex corretto per un tale modello?

Quello che ho cercato:

(.*?)(\.txt) ---> Questo corrisponde a qualsiasi percorso di file termina con .txt.

per escludere i file che contiene _test ho cercato di usare lookahed negativo:

(.*?)(?!_test)(\.txt)

Ma non ha funzionato.

Ho anche provato lookbehind negativo ma MSVC14 (Visual Studio 2015) genera un'eccezione std::regex_error durante la creazione dell'espressione regolare, quindi non sono sicuro se non è supportato o sto utilizzando la sintassi errata.

risposta

2

in base a ciò che hai postato, utilizzare questo modello

^(?!.*_).*\.txt$ 

Demo


o di questo modello sulla base di OP Modifica

^(.*(?<!_test)\.txt$) 

Demo

+0

Grazie, funziona. – Banex

+0

È possibile escludere selettivamente solo i nomi '* _test.txt'? – dlask

+0

@dlask Con "selettivamente" intendi ignorare il 'TEMP' alla fine? Temo che possa succedere che non ci sia '_test' ma un' TEMP'. – Banex

2
^(?!.*?_test\.).*\.txt$ 

Non ho accesso a VS 2015 atm, ma questo utilizza solo lookahead, quindi dovrebbe funzionare.

+0

In senso stretto: non consente "_test.file.txt" - che secondo le specifiche dovrebbe essere consentito. Tuttavia, questa è quasi certamente la soluzione migliore per il caso pratico. –

+0

Suppongo che avrei potuto aggiungere 'txt' al lookahead per risolverlo. –

0

Un trucco per emulare il lookbehind che si vorrebbe davvero (ma che purtroppo non è supportato in C++ 11), è quello di invertire la stringa, quindi utilizzare un lookahead. Il tuo regexp diventerebbe qualcosa di simile

^txt\.(?!tset_).* 

Il problema con il lookahead si è tentato è che esso si applica alla posizione in cui si deve anche iniziare corrispondente al '.txt.' parte. Quindi la parte '(?! _ Test) (. Txt)' del tuo regexp dice 'Voglio qualcosa che non inizia con _test, ma corrisponde .txt'. Qualunque cosa finisca in .txt corrisponderà effettivamente a questo, ed è per questo che non funziona.

Update: una regex con lookbehind negativo (che non funzionerà in C++, ma funziona in python per esempio):

^.*(?<!_test)\.txt$ 
+0

Grazie per la spiegazione. Ho provato a guardare perché mi sono reso conto che lookahead non avrebbe funzionato, come hai confermato. Potresti includere nella tua risposta la regex corretta usando lookbehind? Verificherò se funziona in VS2015 (forse la mia sintassi è errata), e comunque potrebbe essere utile per altre implementazioni di espressioni regolari. – Banex

+0

@Banex: sfortunatamente, non esiste una sintassi corretta per il lookbehind - non è supportato nel dialetto regex richiesto da C++ 11. Vedi anche questa domanda: http://stackoverflow.com/questions/14538687/using-regex-lookbehinds-in-c11 –

1

migliore scommessa? Don't use regexes. In particolare in un caso di ricerca di stringa semplicistico come questo.

In primo luogo ci sono un paio di semplici ottimizzazioni che possono essere fatte secondo i parametri della domanda:

  1. Dal momento che l'ingresso string s' estensione deve essere: '.txt' non abbiamo bisogno di verificare se l'estensione è ".txtTEMP"
  2. L'unica condizione non corrisponde quindi, dove l'input string termina in "_test.txt", richiede di verificare che lo stelo termini in "_test" poiché l'estensione è già nota: ". txt "

Entrambi di questi controlli saranno sempre compensati da un numero fisso di caratteri dalla fine dell'input string. Dal momento che tutte le informazioni per entrambe queste espressioni è noto dovrebbe essere messa a punto in fase di compilazione:

constexpr auto doMatch = ".txt"; 
constexpr auto doMatchSize = strlen(doMatch); 
constexpr auto doNotMatch = "_test"; 
constexpr auto doNotMatchSize = strlen(doNotMatch) + doMatchSize; 

Dato string input potrebbe essere testato per il successo come segue:

if(input.size() >= doMatchSize && 
    equal(input.end() - doMatchSize, input.end(), doMatch) && 
    (input.size() < doNotMatchSize || 
    !equal(input.end() - doNotMatchSize, input.end() - doMatchSize, doNotMatch))) 

Si può vedere un esempio dal vivo qui: http://ideone.com/7BcyFi

Problemi correlati