2014-10-30 11 views
5

Sono nel processo di apprendimento elisir e sono imbattuto in qualcosa che non ha senso per me ...String.replace ritorno rappresentazione binaria di corda

Sto cercando di rimuovere la punteggiatura

"Freude schöner Götterfunken" |> String.replace(~r/[^\s\w]/, "") #=> <<70, 114, 101, 117, 100, 101, 32, 115, 99, 104, 195, 110, 101, 114, 32, 71, 195, 116, 116, 101, 114, 102, 117, 110, 107, 101, 110>> 
"Freude schöner Götterfunken" |> String.replace(~r/[^\w]/, "") #=> <<70, 114, 101, 117, 100, 101, 32, 115, 99, 104, 195, 110, 101, 114, 32, 71, 195, 116, 116, 101, 114, 102, 117, 110, 107, 101, 110>> 
"Freude schöner Götterfunken" |> String.replace(~r/\p{P}/, "") #=> <<70, 114, 101, 117, 100, 101, 32, 115, 99, 104, 195, 110, 101, 114, 32, 71, 195, 116, 116, 101, 114, 102, 117, 110, 107, 101, 110>> 
"Freude schöner Götterfunken" |> String.replace(~r/\s/, "") #=> FreudeschönerGötterfunken 
"Hi my name is bob" |> String.replace(~r/\w/, "") #=> " " 
Regex.run(~r/[^\w]/, "Freude schöner Götterfunken") #=> [<<182>>] 

Questo sembra un insetto, ma essendo un noob sto assumendo l'ignoranza. Perché la sostituzione non restituisce la stringa?

risposta

17

Hai ragione che String.replace/2 non restituisce una stringa poiché Elixir definisce le stringhe come binari codificati utf-8. Tuttavia, questo non è un bug perché Elixir si aspetta che tu passi o esegua operazioni valide sugli argomenti, poiché non verificherà tutti i risultati (in quanto costosi).

Ad esempio, se si passa uno dei binari sopra a String.downcase/1, Elixir esegue il downcase delle parti di cui è a conoscenza, ignorando il resto. La ragione per cui funziona è perché UTF-8 si auto-sincronizza, quindi se vediamo qualcosa di strano, possiamo saltare il byte strano e continuare a fare l'operazione.

In altre parole, la filosofia di gestione delle stringhe in elisir è quella di convalidare i limiti (come quando si aprono file, eseguire I/O o leggere da un database) e assumere che stiamo lavorando e che eseguiamo operazioni valide in tutto.

OK, con tutto ciò che è stato detto, perché il tuo codice non funziona? Il motivo è che la tua espressione regolare non ha unicode abilitato. Aggiungiamo il modificatore u poi:

iex> "Freude schöner Götterfunken" |> String.replace(~r/[^\s\w]/u, "") 
"Freude schöner Götterfunken" 

Beh, non risolve il problema, ma almeno il risultato è valido. Reading about unicode categories here significa che non possiamo veramente risolvere questo problema con proprietà unicode perché ö nell'esempio è un singolo punto di codice che corrisponde alla proprietà \p{L}.

Forse la soluzione più semplice in questo caso, supponendo che si vuole risolvere solo per il tedesco, è quello di attraversare solo il binario mantenendo il byte < = 127. Qualcosa di simile:

iex> for <<x <- "Freude schöner Götterfunken">>, x <= 127, into: "", do: <<x>> 
"Freude schner Gtterfunken" 

Se si desidera una più soluzione completa, probabilmente dovresti esaminare la traslitterazione Unicode.

+0

Risposta stupenda! Grazie per la spiegazione dettagliata. Ho perso il modificatore u durante la lettura dei documenti. – matmer

0

String.replace restituisce una "stringa", ma le stringhe con doppie virgolette vengono effettivamente memorizzate come file binari in elisir. Per qualche motivo, l'output non può essere visualizzato come una stringa normale, quindi torna a visualizzare la rappresentazione binaria.

+1

Il "per qualche motivo" è qui: http://elixir-lang.org/getting_started/6.html#6.3-char-lists. Si noti la seguente frase: "(nota che iex produrrà solo punti codice se uno qualsiasi dei caratteri è al di fuori dell'intervallo ASCII)." Poiché @matmer aveva caratteri che erano al di fuori dell'intervallo ASCII, l'intera cosa veniva visualizzata come punti di codice. –

Problemi correlati