2013-05-23 33 views
5

Tôi có một mã số string[] có chứa mã. Mỗi dòng chứa một số không gian hàng đầu. Tôi cần phải 'unindent' mã càng nhiều càng tốt mà không thay đổi định dạng hiện có.Cách hiệu quả để chặn các dòng mã không được lưu trữ trong một chuỗi

Ví dụ nội dung của tôi string[] có thể

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

Tôi muốn tìm một phương pháp hợp lý thanh lịch và hiệu quả (LINQ?) Để chuyển đổi nó để

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

Để được rõ ràng Tôi đang tìm kiếm

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

Bạn có chắc chắn đó là tất cả không gian, và không phải tab nào? – Servy

+2

Bạn không muốn 'UnindentAsMuchAsPossible' thành" return "' void', phải không? –

+0

Bạn đúng @Tim. Đã sửa. – shamp00

Trả lời

3

xây dựng trên Tim Schmelter của câu trả lời:

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)); 
} 

này xử lý:

  • ký tự tab
  • đang
  • nguồn có chứa dòng rỗng
+0

Hóa ra trường hợp sử dụng của tôi có cả hai tab và các dòng trống. Câu trả lời này đi thêm dặm. – shamp00

1

Điều này đầu tiên sẽ tìm thấy số nhận dạng tối thiểu và sau đó xóa tha t số không gian cho mỗi dòng.

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)); 

Nó sẽ có thể viết tất cả mọi thứ trong một biểu thức duy nhất, nhưng trong khi nó là chức năng hơn nữa tao nhã Tôi nghĩ rằng biến minIndent làm cho mã dễ đọc hơn.

3

Chỉ cần đếm số lượng các không gian hàng đầu trên dòng đầu tiên, và sau đó "loại bỏ" mà nhiều nhân vật ngay từ đầu của mỗi dòng:

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

Điều này giả định dòng đầu tiên ít thụt lề nhất (có thể không phải là trường hợp). –

+0

@MattHouser Nếu chương trình được định dạng đúng để bắt đầu với cách nó có thể không phải là trường hợp? Bạn có ví dụ về chương trình như vậy không? – Servy

+0

Trong câu hỏi ban đầu, mã nguồn có được định dạng đúng để bắt đầu không? Câu hỏi chỉ cho biết rằng định dạng hiện có sẽ được duy trì. –

2

Sử dụng LINQ chút và Regex để tìm ra thụt đầu dòng ngắn nhất, sau đó xóa số ký tự đó khỏi tất cả các dòng.

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); 

Prints:

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

Chương trình này sẽ quét tất cả các chuỗi trong mảng. Nếu bạn có thể giả định rằng dòng đầu tiên là thụt vào ít nhất, sau đó bạn có thể cải thiện hiệu suất bằng cách quét chỉ dòng đầu tiên:

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

Cũng lưu ý rằng điều này sẽ xử lý một ký tự tab ("\t") như một nhân vật duy nhất. Nếu có sự kết hợp giữa các tab và dấu cách, thì việc đảo chiều thụt lề có thể phức tạp. Cách dễ nhất để xử lý điều đó là thay thế tất cả các phiên bản của tab bằng số lượng khoảng trống thích hợp (thường là 4, mặc dù các ứng dụng riêng lẻ có thể khác nhau một cách hoang dại) trước khi chạy mã ở trên.

Cũng có thể sửa đổi mã ở trên để tăng thêm trọng số cho các tab. Tại thời điểm đó, regex không còn được sử dụng nhiều.

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(); 

Prints (giả sử cửa sổ giao diện điều khiển của bạn có chiều rộng tab 8 như tôi):

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

Bạn có thể cần phải sửa đổi mã này để làm việc với cạnh trường hợp kịch bản, chẳng hạn như zero- các dòng hoặc đường dài chỉ chứa khoảng trắng.

3

này nên làm việc:

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

Nó di chuyển mã sang bên trái, tất cả các dòng với cùng một số không gian.

Ví dụ:

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

Chính xác những gì tôi đang nghĩ. Tùy thuộc vào những gì OP muốn, bạn cũng có thể cải thiện điều này bằng cách thay đổi điều này: 'l.TakeWhile (Char.IsWhiteSpace) .Count()' thành: 'l.TakeWhile (Char.IsWhiteSpace) .Sum (c => c == '\ t'? TabWidth: 1) 'trong đó' TabWidth' giống như 4. –

+1

Một cải tiến khác là xử lý các dòng trống. Thông thường, nếu ai đó viết mã có dòng trống, dòng trống sẽ không chứa gì ngoài "\ r \ n". Mã này sẽ không xử lý điều đó. –

1

Để phù hợp với giao diện phương pháp mong muốn của bạn:

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

này được thụt lề tối thiểu của tất cả các dòng (giả định không gian mà thôi, không có tab), sau đó dải minIndent không gian từ đầu của mỗi dòng và trả về giá trị đó là IEnumerable.

Các vấn đề liên quan