2009-09-11 62 views
14

Tôi đang cố gắng tạo kết hợp trình phân tích cú pháp/trình phân tích cú pháp chung.Chuỗi định dạng phân tích cú pháp

kịch bản Ví dụ:

  • Tôi có một chuỗi cho string.Format(), ví dụ var format = "{0}-{1}"
  • Tôi có một mảng đối tượng (chuỗi) cho đầu vào, ví dụ: var arr = new[] { "asdf", "qwer" }
  • Tôi định dạng mảng bằng chuỗi định dạng, ví dụ: var res = string.Format(format, arr)

Điều tôi đang cố gắng thực hiện là hoàn nguyên chuỗi được định dạng lại thành mảng đối tượng (chuỗi). Một cái gì đó như (mã giả):

var arr2 = string.Unformat(format, res) 

// when: res = "asdf-qwer"  
// arr2 should be equal to arr 

Bất kỳ ai có kinh nghiệm làm điều gì đó như thế này? Tôi đang suy nghĩ về việc sử dụng biểu thức thông thường (sửa đổi chuỗi định dạng ban đầu, và sau đó vượt qua nó để Regex.Matches để có được mảng) và chạy nó cho mỗi giữ chỗ trong chuỗi định dạng. Điều này có khả thi hay là có giải pháp nào hiệu quả hơn?

+0

Chuỗi dài chưa được định dạng? –

+0

@Chris: trong giới hạn hợp lý. ATM, tôi chỉ sử dụng tên này trên tên tập tin. –

+0

Lưu ý rằng với tính tổng quát nhất định, kết quả có thể không rõ ràng - ví dụ: 'format = '{0} - {1}'' và 'arr = {" as-df "," qw-er "}'. Có thể bị hủy định dạng theo ba cách khác nhau. Bạn cần xác định cách đối phó với sự mơ hồ hoặc giới hạn nội dung của chuỗi định dạng và giá trị. – peterchen

Trả lời

9

Bạn không thể định dạng được vì thông tin bị mất. String.Format là thuật toán "phá hoại", có nghĩa là bạn không thể (luôn luôn) quay lại.

Tạo lớp mới kế thừa từ string, nơi bạn thêm thành viên theo dõi "{0}-{1}"{ "asdf", "qwer" }, ghi đè ToString() và sửa đổi một chút mã của bạn.

Nếu quá phức tạp, chỉ cần tạo cùng một lớp, nhưng không kế thừa từ string và sửa đổi mã của bạn nhiều hơn một chút.

IMO, đó là cách tốt nhất để thực hiện việc này.

+0

Làm việc nhiều hơn một chút, nhưng rất khả thi. –

2

Đơn giản là không thể trong trường hợp chung. Một số thông tin sẽ bị "mất" (ranh giới chuỗi) theo phương pháp Format. Giả sử:

String.Format("{0}-{1}", "hello-world", "stack-overflow"); 

Bạn sẽ "biến dạng" nó như thế nào?

+0

Điểm tốt. Làm thế nào về việc tạo ra một giải pháp nhỏ hơn so với chung chung có giả định không có nhân vật trong định dạng sẽ có mặt trong mảng các đối tượng? –

+4

Adrian: Điều đó sẽ không rõ ràng trong một số trường hợp: 'String.Format (" {0} {1} "," 12 "," 3 ")' sẽ trả về "123" nhưng bạn không thể suy ra từ chuỗi định dạng rằng đó là "12", "3" hoặc "12", "3" hoặc ... –

+0

Bạn sẽ trả về một loạt kết quả và để khách hàng xử lý nó. – toddmo

2

Giả sử "-" không nằm trong chuỗi gốc, bạn có thể không chỉ sử dụng Split không?

var arr2 = formattedString.Split('-'); 

Lưu ý rằng điều này chỉ áp dụng cho ví dụ được trình bày với giả định. Bất kỳ thuật toán ngược lại phụ thuộc vào loại định dạng được sử dụng; một hoạt động nghịch đảo có thể thậm chí không thể, như được ghi nhận bởi các câu trả lời khác.

+0

Định dạng có thể là bất kỳ thứ gì. Nhưng có, chúng tôi sẽ phải đồng ý rằng bất cứ điều gì trong định dạng sẽ không xuất hiện trên mảng đang được định dạng. –

+0

Đã thêm một số giải thích cho câu trả lời. –

1

Một giải pháp đơn giản có thể là để

  • thay thế tất cả thẻ định dạng với (. *)
  • thoát tất cả charaters đặc biệt khác ở format
  • làm cho trận đấu regex không tham lam

Điều này sẽ giải quyết sự mơ hồ cho trận đấu ngắn nhất có thể.

(Tôi không giỏi RegEx, vì vậy hãy xác tôi, folks :))

0

Sau khi định dạng, bạn có thể đặt chuỗi kết quả và các mảng của các đối tượng vào một cuốn từ điển với chuỗi như là chìa khóa:

Dictionary<string,string []> unFormatLookup = new Dictionary<string,string []> 
... 
var arr = new string [] {"asdf", "qwer" }; 
var res = string.Format(format, arr); 
unFormatLookup.Add(res,arr); 

và trong phương pháp Unformat, bạn chỉ có thể vượt qua một chuỗi và tìm kiếm chuỗi và trả về mảng sử dụng:

string [] Unformat(string res) 
{ 
    string [] arr; 
    unFormatLoopup.TryGetValue(res,out arr); //you can also check the return value of TryGetValue and throw an exception if the input string is not in. 
    return arr; 
} 
14

trong khi các ý kiến ​​về thông tin bị mất có giá trị, thỉnh thoảng Bạn chỉ muốn nhận các giá trị chuỗi của một chuỗi với định dạng đã biết.

Một phương pháp là this blog post được viết bởi một người bạn của tôi. Ông đã triển khai phương pháp mở rộng có tên là string[] ParseExact(), tương tự như DateTime.ParseExact(). Dữ liệu được trả về dưới dạng một chuỗi các chuỗi, nhưng nếu bạn có thể sống với nó, nó rất tiện dụng.

public static class StringExtensions 
{ 
    public static string[] ParseExact(
     this string data, 
     string format) 
    { 
     return ParseExact(data, format, false); 
    } 

    public static string[] ParseExact(
     this string data, 
     string format, 
     bool ignoreCase) 
    { 
     string[] values; 

     if (TryParseExact(data, format, out values, ignoreCase)) 
      return values; 
     else 
      throw new ArgumentException("Format not compatible with value."); 
    } 

    public static bool TryExtract(
     this string data, 
     string format, 
     out string[] values) 
    { 
     return TryParseExact(data, format, out values, false); 
    } 

    public static bool TryParseExact(
     this string data, 
     string format, 
     out string[] values, 
     bool ignoreCase) 
    { 
     int tokenCount = 0; 
     format = Regex.Escape(format).Replace("\\{", "{"); 

     for (tokenCount = 0; ; tokenCount++) 
     { 
      string token = string.Format("{{{0}}}", tokenCount); 
      if (!format.Contains(token)) break; 
      format = format.Replace(token, 
       string.Format("(?'group{0}'.*)", tokenCount)); 
     } 

     RegexOptions options = 
      ignoreCase ? RegexOptions.IgnoreCase : RegexOptions.None; 

     Match match = new Regex(format, options).Match(data); 

     if (tokenCount != (match.Groups.Count - 1)) 
     { 
      values = new string[] { }; 
      return false; 
     } 
     else 
     { 
      values = new string[tokenCount]; 
      for (int index = 0; index < tokenCount; index++) 
       values[index] = 
        match.Groups[string.Format("group{0}", index)].Value; 
      return true; 
     } 
    } 
} 
+0

Điều gì được trả về trong tình huống này: '" a-b-c ".ParseExact (" {0} - {1} - {0} ")'? – Zarepheth

+0

Gợi ý - thay thế 'format = format.Replace (token, string.Format (" (? 'Nhóm {0}'. *) ", TokenCount));' với 'format = format.ReplaceFirst (token, string.Format ("(? 'nhóm {0}'. *)", tokenCount)); format = format.Replace (token, string.Format ("\\ {0}", tokenCount)); '. Điều này sẽ xử lý tốt hơn các chuỗi định dạng sử dụng các tham số đầu vào nhiều lần. ReplaceFirst đến từ: http://stackoverflow.com/questions/141045/how-do-i-replace-the-first-instance-of-a-string-in-net#141076 – Zarepheth

+0

Không thích "abc" .ParseExact ("{0} {1} {2}") và @ "a $ - \ & * b^c" .ParseExact (@ "{0} $ - \\ & * {1}^{ 2} ") – CRice

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