2013-05-23 10 views
5

Ich habe eine string[], die Code enthält. Jede Zeile enthält einige führende Leerzeichen. Ich muss den Code so viel wie möglich einbinden, ohne die bestehende Formatierung zu ändern.Effiziente Möglichkeit, in einer Zeichenfolge gespeicherte Codezeilen zu entfernen

Zum Beispiel wird der Inhalt meiner string[] könnte

           public class MyClass 
             { 
              private bool MyMethod(string s) 
              { 
               return s == ""; 
              } 
             } 

sein Ich möchte eine recht elegante und effiziente Methode finden (LINQ?) Zu transformieren es zu

public class MyClass 
{ 
    private bool MyMethod(string s) 
    { 
     return s == ""; 
    } 
}

Um klar zu sein Ich suche nach

IEnumerable<string> UnindentAsMuchAsPossible(string[] content) 
{ 
    return ???; 
} 
+0

Sind enthält, und keine Tabs? – Servy

+2

Sie möchten nicht, dass "UnindentAsMuchAsPossible" "void" zurückgibt, oder? –

+0

Sie haben Recht @Tim. Fest. – shamp00

Antwort

3

Aufbauend auf Tim Schmelter Antwort:

static IEnumerable<string> UnindentAsMuchAsPossible(IEnumerable<string> input) 
{ 
    const int TabWidth = 4; 

    if (!input.Any()) 
    { 
     return Enumerable.Empty<string>(); 
    } 

    int minDistance = input 
     .Where(line => line.Length > 0) 
     .Min(line => line 
      .TakeWhile(Char.IsWhiteSpace) 
      .Sum(c => c == '\t' ? TabWidth : 1)); 

    return input 
     .Select(line => line.Replace("\t", new string(' ', TabWidth))) 
     .Select(line => line.Substring(Math.Min(l.Length, minDistance)); 
} 

Diese Griffe:

  • Tabulatorzeichen
  • Quellcode, die Sie sicher, dass es alle Räume leer Linien
+0

Stellt sich heraus, dass mein Anwendungsfall sowohl Registerkarten als auch leere Zeilen enthält. Diese Antwort geht eine Extrameile. – shamp00

1

Dies wird zuerst die minimale Ident finden und dann entfernen tha t Anzahl der Leerzeichen für jede Zeile.

var code = new [] { " foo", " bar" }; 

var minIndent = code.Select(line => line.TakeWhile(ch => ch == ' ').Count()).Min(); 
var formatted = code.Select(line => line.Remove(0, minIndent)); 

Es wäre möglich, alles in einem einzigen Ausdruck zu schreiben, aber während es mehr funktionell elegant ist denke ich, dass die minIndent Variable den Code besser lesbar macht.

3

einfach die Anzahl der führenden Leerzeichen in der ersten Zeile zählen, und „entfernen“, dass viele Zeichen vom Anfang jeder Zeile dann:

IEnumerable<string> UnindentAsMuchAsPossible(string[] content) 
{ 
    int spacesOnFirstLine = content[0].TakeWhile(c => c == ' ').Count(); 
    return content.Select(line => line.Substring(spacesOnFirstLine)); 
} 
+1

Dies setzt voraus, dass die erste Zeile am wenigsten eingerückt ist (was möglicherweise nicht der Fall ist). –

+0

@MattHouser Wenn das Programm richtig formatiert ist, um damit zu beginnen, wie könnte es nicht der Fall sein? Hast du ein Beispiel für ein solches Programm? – Servy

+0

Wo in der ursprünglichen Frage heißt es, der Quellcode ist richtig formatiert, um damit zu beginnen? Die Frage besagt nur, dass die bestehende Formatierung beibehalten werden soll. –

2

Verwenden Sie ein wenig LINQ und Regex die kürzeste Vertiefung zu finden, Entfernen Sie dann die Anzahl der Zeichen aus allen Zeilen.

string[] l_lines = { 
         "           public class MyClass", 
         "           {", 
         "            private bool MyMethod(string s)", 
         "            {", 
         "             return s == \"\";", 
         "            }", 
         "           }" 
        }; 

int l_smallestIndentation = 
    l_lines.Min(s => Regex.Match(s, "^\\s*").Value.Length); 

string[] l_result = 
    l_lines.Select(s => s.Substring(l_smallestIndentation)) 
      .ToArray(); 

foreach (string l_line in l_result) 
    Console.WriteLine(l_line); 

Drucke:

public class MyClass 
{ 
    private bool MyMethod(string s) 
    { 
     return s == ""; 
    } 
} 

Dieses Programm wird alle Strings im Array scannen. Wenn Sie davon ausgehen können, dass die erste Zeile des am wenigsten eingerückt ist, dann könnte man die Leistung verbessern durch das Scannen nur die erste Zeile:

int l_smallestIndentation = 
    Regex.Match(l_lines[0], "^\\s*").Value.Length; 

Beachten Sie auch, dass dies ein Tab-Zeichen behandelt ("\t") als ein einzelnes Zeichen. Wenn es eine Mischung aus Tabulatoren und Leerzeichen gibt, kann es schwierig sein, den Einzug umzukehren. Der einfachste Weg, dies zu handhaben, wäre, alle Instanzen von Tabs mit der entsprechenden Anzahl von Leerzeichen (oft 4, obwohl einzelne Anwendungen können wild variieren) zu ersetzen vor mit dem Code oben.

Es wäre auch möglich, den obigen Code zu ändern, um den Registerkarten zusätzliches Gewicht zu verleihen. An diesem Punkt ist die Regex nicht mehr von großem Nutzen.

string[] l_lines = { 
     "\t\t\tpublic class MyClass", 
     "      {", 
     "        private bool MyMethod(string s)", 
     "        {", 
     "  \t  \t\treturn s == \"\";", 
     "        }", 
     "\t\t\t}" 
    }; 

int l_tabWeight = 8; 
int l_smallestIndentation = 
    l_lines.Min 
    (
     s => s.ToCharArray() 
       .TakeWhile(c => Char.IsWhiteSpace(c)) 
       .Select(c => c == '\t' ? l_tabWeight : 1) 
       .Sum() 
    ); 

string[] l_result = 
    l_lines.Select 
    (
     s => 
     { 
      int l_whitespaceToRemove = l_smallestIndentation; 
      while (l_whitespaceToRemove > 0) 
      { 
       l_whitespaceToRemove -= s[0] == '\t' ? l_tabWeight : 1; 
       s = s.Substring(1); 
      } 
      return s; 
     } 
    ).ToArray(); 

Drucke (Ihr Konsolenfenster unter der Annahme, eine Lasche Breite von 8 wie bei mir):

public class MyClass 
{ 
     private bool MyMethod(string s) 
     { 
       return s == ""; 
     } 
} 

Möglicherweise müssen Sie diesen Code ändern mit kanten Case-Szenarien zu arbeiten, wie null- Längenlinien oder Linien, die nur Leerzeichen enthalten.

3

sollte diese Arbeit:

static IEnumerable<string> UnindentAsMuchAsPossible(IEnumerable<string> input) 
{ 
    int minDistance = input.Min(l => l.TakeWhile(Char.IsWhiteSpace).Count()); 
    return input.Select(l => l.Substring(minDistance)); 
} 

Er bewegt den Code nach links, alle Zeilen mit der gleichen Anzahl von Räumen.

Zum Beispiel:

string testString = @"  
        public class MyClass 
        { 
         private bool MyMethod(string s) 
         { 
          return s == ""; 
         } 
        }"; 


string[] lines = testString.Split(new[] { Environment.NewLine }, StringSplitOptions.None); 
string[] unindentedArray = UnindentAsMuchAsPossible(lines).ToArray(); 
+2

Genau das, was ich dachte. Abhängig davon, was das OP will, könntest du das auch verbessern, indem du folgendes änderst: 'l.TakeWhile (Char.IsWhiteSpace) .Count()' zu diesem: 'l.TakeWhile (Char.IsWhiteSpace) .Sum (c => c == '\ t'? TabWidth: 1) 'where' TabWidth' ist etwas wie 4. –

+1

Eine weitere Verbesserung wäre die Handhabung leerer Zeilen. Wenn jemand Code schreibt, der eine leere Zeile enthält, enthält die leere Zeile normalerweise nichts als das "\ r \ n". Dieser Code wird das nicht behandeln. –

1

Um die gewünschte Methode Schnittstelle entsprechen:

IEnumerable<string> UnindentAsMuchAsPossible(string[] content) 
{ 
    int minIndent = content.Select(s => s.TakeWhile(c => c == ' ').Count()).Min(); 
    return content.Select(s => s.Substring(minIndent)).AsEnumerable(); 
} 

Dies erhält die minimale Einrückung aller Linien (annimmt Räume nur, keine tabs), dann streift minIndent Räume von der Anfang jeder Zeile und gibt das als IEnumerable zurück.

Verwandte Themen