2013-03-02 23 views
8

Tôi đang sử dụng Regex sauRegex với phi chụp nhóm trong C#

JOINTS.*\s*(?:(\d*\s*\S*\s*\S*\s*\S*)\r\n\s*)* 

vào loại dữ liệu sau:

JOINTS    DISPL.-X    DISPL.-Y    ROTATION 


    1   0.000000E+00   0.975415E+01   0.616921E+01 
    2   0.000000E+00   0.000000E+00   0.000000E+00 

Ý tưởng là để trích xuất hai nhóm, mỗi dòng chứa một dòng (bắt đầu bằng Mã số, 1, 2, v.v.) Mã C# như sau:

string jointPattern = @"JOINTS.*\s*(?:(\d*\s*\S*\s*\S*\s*\S*)\r\n\s*)*"; 
MatchCollection mc = Regex.Matches(outFileSection, jointPattern); 
foreach (Capture c in mc[0].Captures) 
{ 
    JointOutput j = new JointOutput(); 
    string[] vals = c.Value.Split(); 
    j.Joint = int.Parse(vals[0]) - 1; 
    j.XDisplacement = float.Parse(vals[1]); 
    j.YDisplacement = float.Parse(vals[2]); 
    j.Rotation = float.Parse(vals[3]); 
    joints.Add(j); 
} 

Tuy nhiên, điều này không hoạt động: thay vì trả về hai nhóm được chụp (nhóm bên trong), nó trả về một nhóm: toàn bộ khối, bao gồm các tiêu đề cột. Lý do tại sao điều này xảy ra? C# có giao dịch với các nhóm không bị bắt khác nhau không?

Cuối cùng, RegExes là cách tốt nhất để làm điều này? (Tôi thực sự cảm thấy như tôi có hai vấn đề bây giờ.)

Trả lời

8

mc[0].Captures tương đương với mc[0].Groups[0].Captures. Groups[0] luôn đề cập đến toàn bộ kết quả phù hợp, vì vậy sẽ chỉ bao giờ có một Capture được liên kết với nó. Phần bạn đang tìm kiếm được chụp trong nhóm # 1, vì vậy bạn nên sử dụng mc[0].Groups[1].Captures.

Nhưng regex của bạn được thiết kế để khớp với toàn bộ đầu vào trong một lần thử, do đó phương pháp Matches() sẽ luôn trả về MatchCollection chỉ với một Trận đấu trong đó (giả sử kết quả thành công). Bạn cũng có thể sử dụng thay vì Match():

Match m = Regex.Match(source, jointPattern); 
    if (m.Success) 
    { 
    foreach (Capture c in m.Groups[1].Captures) 
    { 
     Console.WriteLine(c.Value); 
    } 
    } 

đầu ra:

1   0.000000E+00   0.975415E+01   0.616921E+01 
2   0.000000E+00   0.000000E+00   0.000000E+00 
+0

Bạn biết đấy, tôi thực sự đã kiểm tra MSDN để tìm hiểu xem thuộc tính 'Captures' hoạt động như thế nào (tôi chưa bao giờ sử dụng nó), và tôi đã không chú ý rằng nó đề cập đến nhóm 0 (rõ ràng là nguyên nhân chính gây ra sự cố cho OP). +1! – Cameron

1

Có hai vấn đề: Phần lặp lại (?:...) không khớp đúng cách; và .* là tham lam và tiêu thụ tất cả các đầu vào, do đó, phần lặp lại không bao giờ phù hợp ngay cả khi nó có thể.

Sử dụng này để thay thế:

JOINTS.*?[\r\n]+(?:\s*(\d+\s*\S*\s*\S*\s*\S*)[\r\n\s]*)* 

này có một bộ phận lãnh đạo không tham lam, đảm bảo rằng phần dòng khớp bắt đầu trên một dòng mới (không ở giữa một tiêu đề), và sử dụng [\r\n\s]* trong trường hợp các dòng mới không chính xác như bạn mong đợi.

Cá nhân, tôi sẽ sử dụng regexes cho điều này, nhưng tôi thích regexes :-) Nếu bạn tình cờ biết rằng cấu trúc của chuỗi sẽ luôn là [title] [newline] [newline] [lines] thì có lẽ nó nhiều hơn đơn giản (nếu ít linh hoạt hơn) chỉ cần chia nhỏ trên các dòng mới và xử lý tương ứng.

Cuối cùng, bạn có thể sử dụng regex101.com hoặc một trong nhiều trang web kiểm tra regex khác để giúp gỡ lỗi các cụm từ thông dụng của bạn.

+0

Nope, vẫn không hoạt động.Nó cung cấp cho một nhóm chụp lớn chứa mọi thứ từ JOINTS đến số dấu chấm động cuối cùng. – ian93

+0

@ ian93: Thử ngay bây giờ, tôi đã sửa lỗi xử lý dòng/bắt đầu xử lý dòng mới. Ngoài ra, tại sao bạn sử dụng 'Trận đấu' nếu bạn biết chỉ có một trận đấu? – Cameron

+0

@Cameron nó sẽ chỉ khớp với chữ cái đầu tiên, thêm '*' hoặc '+' vào cuối – tttony

2

Tôi sẽ không sử dụng Regex để nâng và phân tích cú pháp văn bản.

var data = @"  JOINTS    DISPL.-X    DISPL.-Y    ROTATION 


     1   0.000000E+00   0.975415E+01   0.616921E+01 
     2   0.000000E+00   0.000000E+00   0.000000E+00"; 

var lines = data.Split('\r', '\n').Where(s => !string.IsNullOrWhiteSpace(s)); 
var regex = new Regex(@"(\S+)"); 

var dataItems = lines.Select(s => regex.Matches(s)).Select(m => m.Cast<Match>().Select(c => c.Value)); 

enter image description here

+0

Tôi nghĩ rằng tôi có thể đi với cách tiếp cận này, nhưng sử dụng ít LINQ, vì tôi phải duy trì mã và nhìn vào mã của bạn Tôi không có ý tưởng gì đang xảy ra ... – ian93

+3

Có thể muốn học LINQ vì nó thực sự mạnh mẽ. Các dòng cuối cùng phù hợp với mỗi dòng và kéo ra tất cả mọi thứ mà không phải là một không gian và sau đó trích xuất các giá trị từ 'CaptureCollection' đó là bên trong' MatchCollection'. – Romoku

+5

Tôi thừa nhận, nó là kinda funny khi một chàng trai sử dụng regex báo cáo bác bỏ một tuyên bố linq cho tìm kiếm phức tạp. Có sức mạnh trong ngắn gọn, nó áp dụng cho linq cũng như regexes. –

1

Tại sao không chỉ nắm bắt được giá trị và bỏ qua phần còn lại. Đây là một regex được các giá trị.

string data = @"JOINTS DISPL.-X DISPL.-Y ROTATION 
1 0.000000E+00 0.975415E+01 0.616921E+01 
2 0.000000E+00 0.000000E+00 0.000000E+00"; 

string pattern = @"^ 
\s+ 
(?<Joint>\d+) 
\s+ 
(?<ValX>[^\s]+) 
\s+ 
(?<ValY>[^\s]+) 
\s+ 
(?<Rotation>[^\s]+)"; 

var result = Regex.Matches(data, pattern, RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace | RegexOptions.ExplicitCapture) 
        .OfType<Match>() 
        .Select (mt => new 
        { 
        Joint = mt.Groups["Joint"].Value, 
        ValX = mt.Groups["ValX"].Value, 
        ValY = mt.Groups["ValY"].Value, 
        Rotation = mt.Groups["Rotation"].Value, 
        }); 
/* result is 
IEnumerable<> (2 items) 
Joint ValX ValY Rotation 
1 0.000000E+00 0.975415E+01 0.616921E+01 
2 0.000000E+00 0.000000E+00 0.000000E+00 
*/