2014-06-22 18 views
5

Sto cercando un codice regex con questo schema:lettere Partita alfa e lettere accentate alfa

  • deve contenere almeno 1 dei seguenti e corrispondere l'intera stringa.

  • possono contenere solo caratteri alfa (a-z A-Z) ...

  • e lettere alfa accentate (A bis à ecc).

sto usando preg_match('/^([\p{L}]*)$/iu', $input), ma \p{L} partite tutte le lettere Unicode, tra cui cinese. Voglio solo consentire le lettere dell'alfabeto inglese ma anche le varianti accentate di loro.

Così JohnDoe, Fübar, Lòrem, FírstNäme, Çákë sarebbero tutti gli ingressi validi, perché tutti contengono almeno 1 lettera alfa e/o le lettere accentate alfa, e l'intero partite stringa.

+0

Avete dati di input di esempio da confrontare? – JakeGould

+0

[[: alpha:]] non lo risolve? –

+0

@AfonsoTsukamoto Provalo. Corrisponderà comunque a cinesi e caratteri giapponesi. – JakeGould

risposta

0

Le lettere alfa accentate sono caratteri unicode non completamente correlati alle varianti non accentate. Potrebbe essere visto in relazione l'un l'altro da un osservatore umano, ma un computer non avrà modo di conoscere la differenza confrontandolo con i caratteri alfa del set standard ASCII. Un modo per ottenere questo è fornendo alla tua espressione regolare da una whitelist di cui sono ammessi i caratteri. Nel caso in cui non hai bisogno dei caratteri accentati, un altro modo sta usando una libreria come Apache Lucene (che è una libreria Java, ma penso di averlo letto può essere utilizzato in PHP) sostituirà i caratteri accentati per le loro varianti non accentate con l'analizzatore/i giusto.

+1

Non vero al 100%. È possibile utilizzare gli script Unicode predefiniti per far corrispondere specifici gruppi di caratteri Unicode come spiegato qui. http://www.regular-expressions.info/unicode.html#category – JakeGould

+0

@JakeGuida trovare. Ma questo non fallirebbe se ottenessi l'input non formattato come lettera + segno, ma solo come lettera? –

+0

Non 100% chiaro su cosa intendi. Guarda la mia risposta dove spiego cosa '\ p {Common}' è uguale a '\ p {Latin}'. In ultima analisi, si può davvero confrontare solo in ampie aree predefinite. Ma ci sono modi per farlo a seconda dell'utilità e delle esigenze del tuo script. – JakeGould

1

Utilizzando i dati di esempio e alcuni caratteri cinesi &, la regex /[!\p{Common}\p{Latin}]*/iu sembra funzionare. Per maggiori dettagli, questo sito ha uno sfondo piacevole su predefined Unicode categories e un bel simple explanation here. Ma restituisce una corrispondenza vuota per i caratteri non latini in questa versione. L'uno nella mia EDIT: in fondo sarà meglio per la logica booleana pulita:

// Set a test array. 
$test_array = array(); 
$test_array[] = 'JóhnDoe'; 
$test_array[] = 'Fübar'; 
$test_array[] = 'Lòrem'; 
$test_array[] = 'FírstNäme'; 
$test_array[] = '•••••••'; 
$test_array[] = 'Çákë'; 
$test_array[] = '形声字/形聲字'; 
$test_array[] = 'ラドクリフ、マラソン'; 

// Set the header for debugging output. 
header('Content-Type: text/plain; charset=utf-8'); 

// Roll through the test array. 
foreach ($test_array as $test_value) { 

    // Run a regex to detect latin and common characters. 
    preg_match('/[!\p{Common}\p{Latin}]*/iu', $test_value, $matches); 

    // Kludge using array filtering to get rid of empty matches. 
    $matches = array_filter($matches); 

    // Dump the matches for debugging. 
    print_r($matches); 

} 

L'uscita è la seguente: Si noti come i cinesi & caratteri giapponesi tornano partite vuote. Notare anche come il corrispondesse allo \p{Common} corrispondenza regex. Se non si desidera che vengano visualizzati caratteri comuni come questo, è sufficiente modificare la regex per essere, /[!\p{Latin}]*/iu. Sto usando array_filter per pulire quei valori vuoti, ma è kludgy. Quindi questo non è perfetto, ma utile:

Array 
(
    [0] => JóhnDoe 
) 
Array 
(
    [0] => Fübar 
) 
Array 
(
    [0] => Lòrem 
) 
Array 
(
    [0] => FírstNäme 
) 
Array 
(
    [0] => ••••••• 
) 
Array 
(
    [0] => Çákë 
) 
Array 
(
) 
Array 
(
) 

EDIT: Questo codice test utilizza una variante del regex ho postato qui sopra per eliminare il problema non-partita vuoto sopra; /(?:[\p{Latin}])+/iu. Nota che funziona solo corrispondendo allo \p{Latin} quindi \p{Common} non giocherà bene qui. Ma i risultati utilizzando /(?:[\p{Latin}])+/iu sono più puliti e garantiscono è possibile utilizzare un semplice controllo booleano di preg_match per verificare la presenza di caratteri invece di dover utilizzare un kludge che mescola la matrice $matches con array_filter:

// Set a test array. 
$test_array = array(); 
$test_array[] = 'JóhnDoe'; 
$test_array[] = 'Fübar'; 
$test_array[] = 'Lòrem'; 
$test_array[] = 'FírstNäme'; 
$test_array[] = '•••••••'; 
$test_array[] = 'Çákë'; 
$test_array[] = '形声字/形聲字'; 
$test_array[] = 'ラドクリフ、マラソン'; 

// Set the header for debugging output. 
header('Content-Type: text/plain; charset=utf-8'); 

// Roll through the test array. 
foreach ($test_array as $test_value) { 

    // Run a regex to detect latin and common characters. 
    preg_match('/(?:[\p{Latin}])+/iu', $test_value, $matches); 

    // Dump the matches for debugging. 
    print_r($matches); 

} 

E i nuovi risultati sono i segue.Si noti che le matrici vuote sono veramente vuote e il prey_match restituirà un valore booleano false in quei casi:

Array 
(
    [0] => JóhnDoe 
) 
Array 
(
    [0] => Fübar 
) 
Array 
(
    [0] => Lòrem 
) 
Array 
(
    [0] => FírstNäme 
) 
Array 
(
) 
Array 
(
    [0] => Çákë 
) 
Array 
(
) 
Array 
(
) 
+0

dopo aver letto la risposta zx81 postata più tardi, penso che meriti il ​​massimo in questa domanda. La sua espressione elegante e compatta con il mio aggiornamento ora funzionerà in PCRE, JavaScript e Python, può essere davvero utile per qualcun altro –

+1

@JuanGarcia Fine. Questa non è una competizione. In realtà funziona meglio per non restituire serie vuote come la mia regex, quindi faccio solo +1 per quello. Ma il fatto che il mio non funzioni in JavaScript è un non-problema. Il poster originale sta cercando una soluzione PHP. E la mia risposta è un modo per affrontare questo problema.E su Stack Overflow è positivo per più risposte, poiché se qualcuno lo supera in futuro, è opportuno che rivedano tutte le opzioni valide. Il mio è un'opzione valida così come la soluzione di 'zx81'. – JakeGould

+0

Non dovrebbe essere valido quando inserisco qualcosa come '! Jóhn'. L'intera stringa dovrebbe essere solo lettere alfa e/o varianti accentate. –

3

vorrei suggerire questo regex compatta:

(?i)(?:(?![×Þß÷þø])[a-zÀ-ÿ])+ 

Vedi demo.

  1. Questa espressione regolare sfrutta il fatto che le lettere accentate si desidera che tutti sembrano vivere nella gamma di caratteri Unicode À-ÿ (vedere questo table), quindi abbiamo semplicemente aggiungere alla classe di caratteri.
  2. Lo À-ÿ ha alcuni caratteri indesiderati. A differenza di alcuni motori, PCRE (motore regex di PHP) non supporta classe di caratteri sottrazione, ma noi imitare con il lookahead negativo (?![×Þß÷þø])
  3. essere consapevoli del fatto che alcuni personaggi come à possono essere espresse da diversi punti di codice Unicode (il à grafema, o un a con accento grave). Questo corrisponderà solo ai grafemi non combinati. Catturare tutte le varianti è davvero difficile.

Nel codice:

$regex = "~(?i)(?:(?![×Þß÷þø])[a-zÀ-ÿ])+~u"; 
$hit = preg_match($regex,$subject,$match); 
+0

È fantastico! Non funzionava in JavaScript, questa modifica funziona in PCRE, JavaScript e Python: (?: (?! [× ßß ÷ øø]) [a-zA-ZÀ-ÿ]) + –

+0

@JuanGarcia Sì, questo è un bel modo per farlo. Io uso spesso questo metodo per intervalli personalizzati. Grazie per l'upvote btw (presumo che fossi tu. :)) – zx81

+0

Per favore, aggiorna la risposta per supportare tutti. Ciò può essere utile per qualcun altro –

1

mi si avvicinò con la seguente soluzione utilizzando una combinazione di preg_match e iconv. Testato con PHP 5.5 su Windows e Linux:

$testWords = array(
    // pass 
    'Çákë', 
    'JohnDoe', 
    'Fübar', 
    'Lòrem', 
    'FírstNäme', 
    // fail 
    'Ç@kë', 
    'J0hnDoe', 
    'F行bar', 
    'L高rem', 
    'F前rstNäme', 
    'Ç学kë', 
    '0' 
); 

$matchedWords = array_filter($testWords, function ($word) { 
    // these characters should not be in the search string but may appear after iconv conversion 
    $regexCharsNot = '\^~"`\''; 

    $valid = false; 

    if (!preg_match("/[$regexCharsNot]/u", $word)) { 
     if ($word = @iconv('UTF-8', 'ASCII//TRANSLIT', $word)) { 
      $valid = preg_match("/^[A-Za-z$regexCharsNot]+$/u", $word); 
     } 
    } 

    return $valid; 
}); 

echo print_r($matchedWords, true); 

/* 
Array 
(
    [0] => Çákë 
    [1] => JohnDoe 
    [2] => Fübar 
    [3] => Lòrem 
    [4] => FírstNäme 
) 
*/ 

iconv e ASCII//TRANSLIT introduce i caratteri estranei, che è il motivo per cui è richiesta la doppia $regexCharsNot convalida. Ho trovato questa lista usando quanto segue:

// mb_str_split regex   http://www.php.net/manual/en/function.mb-split.php#99851 
// list of accented characters http://fasforward.com/list-of-european-special-characters/ 

$accentedCharacters = preg_split(
    '/(?<!^)(?!$)/u', 
    'ÄäÀàÁáÂâÃãÅåĄąĂăÆæÇçĆćĈĉČčĎđĐďðÈèÉéÊêËëĚěĘęĜĝĢģĤĥÌìÍíÎîÏïĴĵĶķĹĺĻļŁłĽľÑñŃńŇňÖöÒòÓóÔôÕõŐőØøŒœŔŕŘřߌśŜŝŞşŠšŤťŢţÞþÜüÙùÚúÛûŰűŨũŲųŮůŴŵÝýŸÿŶŷŹźŽžŻż'); 

/* 
$unsupported = ''; // 'Ǎǎẞ'; 

foreach ($accentedCharacters as $c) { 
    if ([email protected]('UTF-8', 'ASCII//TRANSLIT', $c)) { 
     $unsupported .= $c; 
    } 
} 
*/ 
+0

+1 per un'idea interessante, ma intendevi per '^ ~" 'essere' [^ ~ "]' (errore)? – zx81