2009-07-24 16 views
13

Vorrei sostituire il | con OR solo in termini non quotate, ad esempio:Regex: come determinare il numero dispari/pari di occorrenze di un carattere che precede un dato carattere?

"this | that" | "the | other" -> "this | that" OR "the | other" 

Sì, potrei spaccare sullo spazio o di citazione, ottenere un array e iterare attraverso di essa, e ricostruire la stringa, ma che sembra ... inelegante. Quindi, forse c'è un modo regolare per fare questo contando " s precedente | e ovviamente strano significa che il | è quotato e significa anche non quotato. (Nota: l'elaborazione non viene avviata finché non è presente un numero pari di " se esiste almeno uno ").

+6

Regex non farlo . Le librerie Regex che fanno uso di algoritmi che non si basano su espressioni regolari e non possono garantire la stessa efficienza. La risposta di Sinan si basa sull'osservazione che i caratteri della pipa che vuoi cambiare appaiono sempre tra due virgolette e che i caratteri della pipa che non vuoi cambiare non fanno mai. È una buona soluzione, se valida. Altrimenti, abbandona regex. –

+0

Il testo della domanda ha una risposta. Il titolo della domanda no. –

risposta

4

I regex non contano. Ecco a cosa servono i parser.

+1

Sì, questo problema scansiona per una macchina a stati. –

4

È possibile trovare lo Perl FAQ on this issue pertinente.

#!/usr/bin/perl 

use strict; 
use warnings; 

my $x = qq{"this | that" | "the | other"}; 
print join('" OR "', split /" \| "/, $x), "\n"; 
0

Forse siete alla ricerca di qualcosa di simile:

(?<=^([^"]*"[^"]*")+[^"|]*)\| 
1

Non è necessario contare, perché non lo fai citazioni nido. Questo farà:

#!/usr/bin/perl 

my $str = '" this \" | that" | "the | other" | "still | something | else"'; 
print "$str\n"; 

while($str =~ /^((?:[^"|\\]*|\\.|"(?:[^\\"]|\\.)*")*)\|/) { 
     $str =~ s/^((?:[^"|\\]*|\\.|"(?:[^\\"]|\\.)*")*)\|/$1OR/; 
} 

print "$str\n"; 

Ora, spieghiamo quell'espressione.

^ -- means you'll always match everything from the beginning of the string, otherwise 
     the match might start inside a quote, and break everything 

(...)\| -- this means you'll match a certain pattern, followed by a |, which appears 
      escaped here; so when you replace it with $1OR, you keep everything, but 
      replace the |. 

(?:...)* -- This is a non-matching group, which can be repeated multiple times; we 
      use a group here so we can repeat multiple times alternative patterns. 

[^"|\\]* -- This is the first pattern. Anything that isn't a pipe, an escape character 
      or a quote. 

\\.  -- This is the second pattern. Basically, an escape character and anything 
      that follows it. 

"(?:...)*" -- This is the third pattern. Open quote, followed by a another 
       non-matching group repeated multiple times, followed by a closing 
       quote. 

[^\\"] -- This is the first pattern in the second non-matching group. It's anything 
      except an escape character or a quote. 

\\.  -- This is the second pattern in the second non-matching group. It's an 
      escape character and whatever follows it. 

Il risultato è il seguente:

" this \" | that" | "the | other" | "still | something | else" 
" this \" | that" OR "the | other" OR "still | something | else" 
0

Grazie a tutti. Scuse per trascurando di menzionare questo è in javascript e che i termini non devono essere citato, e non ci può essere un qualsiasi numero di termini citati/non quotate, ad esempio:

"this | that" | "the | other" | yet | another -> "this | that" OR "the | other" OR yet OR another 

Daniel, sembra che è nel campo da baseball, cioè fondamentalmente un ciclo di matching/massaging. Grazie per la spiegazione dettagliata. In js, sembra una divisione, un ciclo forEach sull'array di termini, che spinge un termine (dopo aver cambiato un termine in OR) in un array e un re join.

11

È vero che le regex non possono contare, ma è possibile utilizzare per determinare se c'è un numero pari o dispari di qualcosa. Il trucco in questo caso è di esaminare le virgolette dopo il il tubo, non prima di esso.

str = str.replace(/\|(?=(?:(?:[^"]*"){2})*[^"]*$)/g, "OR"); 

Rottura che verso il basso, (?:[^"]*"){2} corrisponde la prossima coppia di virgolette se ce n'è uno, insieme con le intermedie non-citazioni. Dopo averlo fatto tutte le volte che è possibile (che potrebbe essere zero), [^"]*$ consuma tutte le rimanenti non citazioni fino alla fine della stringa.

Naturalmente, questo presuppone che il testo sia ben formato. Non affronta il problema delle virgolette di escape, ma può esserlo se ne hai bisogno.

1

Un altro approccio (simile alla risposta di lavoro di Alan M):

str = str.replace(/(".+?"|\w+)\s*\|\s*/g, '$1 OR '); 

La parte all'interno del primo gruppo (distanziati per migliorare la leggibilità):

".+?" | \w+ 

... in pratica significa, qualcosa citato, o una parola. Il resto significa che è stato seguito da un "|" avvolto in spazi bianchi opzionali. La sostituzione è quella prima parte ("$ 1" indica il primo gruppo) seguito da "OR".

0

@Alan M, funziona bene, la fuga non è necessaria a causa della scarsità di funzionalità FTS sqlite.

@epost, soluzione accettata per brevità ed eleganza, grazie. aveva bisogno di essere semplicemente messo in una forma più generale per unicode, ecc

(".+?"|[^\"\s]+)\s*\|\s* 
0

La mia soluzione in C# per contare le citazioni e poi regex per ottenere le partite:

 // Count the number of quotes. 
     var quotesOnly = Regex.Replace(searchText, @"[^""]", string.Empty); 
     var quoteCount = quotesOnly.Length; 
     if (quoteCount > 0) 
     { 
      // If the quote count is an odd number there's a missing quote. 
      // Assume a quote is missing from the end - executive decision. 
      if (quoteCount%2 == 1) 
      { 
       searchText += @""""; 
      } 

      // Get the matching groups of strings. Exclude the quotes themselves. 
      // e.g. The following line: 
      // "this and that" or then and "this or other" 
      // will result in the following groups: 
      // 1. "this and that" 
      // 2. "or" 
      // 3. "then" 
      // 4. "and" 
      // 5. "this or other" 
      var matches = Regex.Matches(searchText, @"([^\""]*)", RegexOptions.Singleline); 
      var list = new List<string>(); 
      foreach (var match in matches.Cast<Match>()) 
      { 
       var value = match.Groups[0].Value.Trim(); 
       if (!string.IsNullOrEmpty(value)) 
       { 
        list.Add(value); 
       } 
      } 

      // TODO: Do something with the list of strings. 
     } 
Problemi correlati