2016-10-31 5 views
-2

Ich schreibe einen Parser für mein eigenes Markup und ich muss ein paar Escape-Sequenzen behandeln, aber ich bin mir nicht sicher, welche Strategie ich wählen sollte.Wie analysiert man eine Escape-Sequenz?

Insbesondere habe ich zwei in meinen Gedanken.

Hier ist ein Beispiel foo \\\<bar baz mit zwei von ihnen: \\ und \<.

Wenn ich jetzt die Zeichenfolge char

durch char scannen
  1. soll ich erkennen den Backslash \ und dann prüfen, ob das nächste Zeichen ein excapable eines oder
  2. soll ich für den Charakter überprüfen und dann zurückblicken um zu sehen, ob vor einem Backslash \ steht?

Gibt es Haupt (dis) Vorteile in beiden?

+0

Aus der Spitze meines Kopfes wäre es besser, nach einem Blackslash zu suchen, weil das Auffinden aller Blackslashes gegen das Finden aller ausweichbaren Zeichen effizienter scheint –

+0

Was Unescaping versuchen Sie zu implementieren? Viele verschiedene Dinge benutzen Backslashes um zu entkommen, aber oft auf verschiedene Arten. –

+0

@JonSkeet Ich experimentiere mit meinem eigenen Markup und mehrere Charaktere werden in einem bestimmten Kontext eine besondere Bedeutung haben. Ich brauche einen Weg, um ihnen zu entkommen. – t3chb0t

Antwort

2

Tun Sie auch nicht. Option # 2 ist wirklich schlecht, denn wenn Sie auf das vorherige Zeichen zurückblicken und es sich um einen Backslash handelt, woher wissen Sie dann, ob es sich um einen umgekehrten Backslash handelt oder ob es wirklich dem aktuellen Zeichen entgeht?

Sie müssen wissen, wo Sie sind. Der Weg dazu ist eine Zustandsmaschine. Wenn Sie nur \r, \t, \n, \" und \\ tun, können Sie mit einem sehr einfachen erhalten. Wie folgt aus (fiddle here):

public static class StringExtensions 
{ 
    private enum UnescapeState 
    { 
     Unescaped, 
     Escaped 
    } 

    public static String Unescape(this String s) 
    { 
     var sb = new System.Text.StringBuilder(); 
     UnescapeState state = UnescapeState.Unescaped; 

     foreach (var ch in s) 
     { 
      switch (state) 
      { 
       case UnescapeState.Escaped: 
        switch (ch) 
        { 
         case 't': 
          sb.Append('\t'); 
          break; 
         case 'n': 
          sb.Append('\n'); 
          break; 
         case 'r': 
          sb.Append('\r'); 
          break; 

         case '\\': 
         case '\"': 
          sb.Append(ch); 
          break; 

         default: 
          throw new Exception("Unrecognized escape sequence '\\" + ch + "'"); 

         // Finally, what about stuff like '\x0a'? That's a much more 
         // complicated state machine. When you see 'x' in Escaped state, 
         // you transition to UnescapeState.HexDigit0, then either 
         // UnescapeState.HexDigit1 or throw an exception, etc. 
         // Wicked fun to write. 
        } 
        state = UnescapeState.Unescaped; 
        break; 

       case UnescapeState.Unescaped: 
        if (ch == '\\') 
        { 
         state = UnescapeState.Escaped; 
        } 
        else 
        { 
         sb.Append(ch); 
        } 
        break; 
      } 
     } 

     if (state == UnescapeState.Escaped) 
     { 
      throw new Exception("Unterminated escape sequence"); 
     } 

     return sb.ToString(); 
    } 
} 

Der eine große Sache hier ist, dass Sie verschiedene „Akkumulator“ Variablen wie sb haben kann, oder eine hexadezimale Ziffern zu akkumulieren, wenn Sie hex char entkommt wie \x0a tun, aber Sie Habe keine Flaggen. Die state Variable sollte die einzige Möglichkeit sein, den Zustand zu überwachen, in dem Sie sich befinden. Fügen Sie einfach weitere Status zur Enumeration hinzu. Wörtlich sind die dritte Hexadezimalziffer und die vierte Hexadezimalziffer unterschiedliche Zustandswerte in der Aufzählung.

Folgen Sie dieser Regel gedankenlos und Sie können erstaunlich komplizierte Zustandsautomaten mit einem erstaunlich niedrigen IQ und der Aufmerksamkeitsspanne einer Mücke (ich bin der Beweis) schreiben und es nicht vermasseln.

Verwandte Themen