2012-06-14 4 views
7

sto cercando di scrivere due funzioni escape(text, delimiter) e unescape(text, delimiter) con le seguenti proprietà:Qual è l'algoritmo più semplice per sfuggire a un singolo personaggio?

  1. Il risultato di escape non contiene delimiter.

  2. unescape è il contrario della escape, vale a dire

    unescape(escape(text, delimiter), delimiter) == text 
    

    per tutti i valori di text e delimiter

E 'OK per limitare i valori consentiti di delimiter.


Sfondo: Voglio creare una stringa delimitatore di valori separati. Per essere in grado di estrarre di nuovo la stessa lista dalla stringa, devo assicurarmi che le singole stringhe separate non contengano il separatore.


Quello che ho provato: mi si avvicinò con una soluzione semplice (pseudo-codice):

escape(text, delimiter): return text.Replace("\", "\\").Replace(delimiter, "\d") 
unescape(text, delimiter): return text.Replace("\d", delimiter).Replace("\\", "\") 

ma abbiamo scoperto che la proprietà non è riuscita 2 sulla stringa di prova "\d<delimiter>". Attualmente, ho il seguente soluzione di lavoro

escape(text, delimiter): return text.Replace("\", "\b").Replace(delimiter, "\d") 
unescape(text, delimiter): return text.Replace("\d", delimiter).Replace("\b", "\") 

che sembra funzionare, a patto che delimiter non è \, b o d (che va bene, non voglio usare quelli come delimitatori in ogni caso). Tuttavia, poiché non ho formalmente dimostrato la sua correttezza, temo di aver dimenticato alcuni casi in cui una delle proprietà è stata violata. Poiché questo è un problema così comune, presumo che ci sia già un algoritmo di "ben collaudato e corretto" per questo, quindi la mia domanda (vedi titolo).

+2

Escaping? Affidabile, semplice e veloce? Lol. – Will

+1

Qual è il problema con l'escape affidabile, semplice e veloce? –

risposta

3

Il tuo primo algoritmo è corretto.

L'errore è nella realizzazione di unescape(): è necessario sostituire sia \d da delimiter e \\ da \, nello stesso passaggio. Non è possibile utilizzare più chiamate a Sostituisci() come questo.

Ecco un po 'di codice C# per aver citato sicura di stringhe delimitatori separate:

static string QuoteSeparator(string str, 
     char separator, char quoteChar, char otherChar) // "~" -> "~~"  ";" -> "~s" 
    { 
     var sb = new StringBuilder(str.Length); 
     foreach (char c in str) 
     { 
      if (c == quoteChar) 
      { 
       sb.Append(quoteChar); 
       sb.Append(quoteChar); 
      } 
      else if (c == separator) 
      { 
       sb.Append(quoteChar); 
       sb.Append(otherChar); 
      } 
      else 
      { 
       sb.Append(c); 
      } 
     } 
     return sb.ToString(); // no separator in the result -> Join/Split is safe 
    } 
    static string UnquoteSeparator(string str, 
     char separator, char quoteChar, char otherChar) // "~~" -> "~"  "~s" -> ";" 
    { 
     var sb = new StringBuilder(str.Length); 
     bool isQuoted = false; 
     foreach (char c in str) 
     { 
      if (isQuoted) 
      { 
       if (c == otherChar) 
        sb.Append(separator); 
       else 
        sb.Append(c); 
       isQuoted = false; 
      } 
      else 
      { 
       if (c == quoteChar) 
        isQuoted = true; 
       else 
        sb.Append(c); 
      } 
     } 
     if (isQuoted) 
      throw new ArgumentException("input string is not correctly quoted"); 
     return sb.ToString(); // ";" are restored 
    } 

    /// <summary> 
    /// Encodes the given strings as a single string. 
    /// </summary> 
    /// <param name="input">The strings.</param> 
    /// <param name="separator">The separator.</param> 
    /// <param name="quoteChar">The quote char.</param> 
    /// <param name="otherChar">The other char.</param> 
    /// <returns></returns> 
    public static string QuoteAndJoin(this IEnumerable<string> input, 
     char separator = ';', char quoteChar = '~', char otherChar = 's') 
    { 
     CommonHelper.CheckNullReference(input, "input"); 
     if (separator == quoteChar || quoteChar == otherChar || separator == otherChar) 
      throw new ArgumentException("cannot quote: ambiguous format"); 
     return string.Join(new string(separator, 1), (from str in input select QuoteSeparator(str, separator, quoteChar, otherChar)).ToArray()); 
    } 

    /// <summary> 
    /// Decodes the strings encoded in a single string. 
    /// </summary> 
    /// <param name="encoded">The encoded.</param> 
    /// <param name="separator">The separator.</param> 
    /// <param name="quoteChar">The quote char.</param> 
    /// <param name="otherChar">The other char.</param> 
    /// <returns></returns> 
    public static IEnumerable<string> SplitAndUnquote(this string encoded, 
     char separator = ';', char quoteChar = '~', char otherChar = 's') 
    { 
     CommonHelper.CheckNullReference(encoded, "encoded"); 
     if (separator == quoteChar || quoteChar == otherChar || separator == otherChar) 
      throw new ArgumentException("cannot unquote: ambiguous format"); 
     return from s in encoded.Split(separator) select UnquoteSeparator(s, separator, quoteChar, otherChar); 
    } 
0

Forse si può avere un sostituto alternativa per il caso in cui il delimitatore fa inizio con \, b o d. Utilizzare la stessa alternativa alternativa nell'algoritmo unescape

Problemi correlati