<p>I am attempting to parse a string like the following using a .NET regular expression:</p>

<pre><code>H3Y5NC8E-TGA5B6SB-2NVAQ4E0
</code></pre>

<p>and return the following using Split:
    H3Y5NC8E
    TGA5B6SB
    2NVAQ4E0</p>

<p>I validate each character against a specific character set (note that the letters 'I', 'O', 'U' &amp; 'W' are absent), so using string.Split is not an option. The number of characters in each group can vary and the number of groups can also vary. I am using the following expression:</p>

<pre><code>([ABCDEFGHJKLMNPQRSTVXYZ0123456789]{8}-?){3}
</code></pre>

<p>This will match exactly 3 groups of 8 characters each. Any more or less will fail the match.
This works insofar as it correctly matches the input. However, when I use the Split method to extract each character group, I just get the final group. RegexBuddy complains that I have repeated the capturing group itself and that I should put a capture group around the repeated group. However, none of my attempts to do this achieve the desired result. I have been trying expressions like this:</p>

<pre><code>(([ABCDEFGHJKLMNPQRSTVXYZ0123456789]{8})-?){4}
</code></pre>

<p>But this does not work.</p>

<p>Since I generate the regex in code, I could just expand it out by the number of groups, but I was hoping for a more elegant solution. </p>

<hr>

<p>Please note that the character set does not include the entire alphabet. It is part of a product activation system. As such, any characters that can be accidentally interpreted as numbers or other characters are removed. e.g. The letters 'I', 'O', 'U' &amp; 'W' are not in the character set.</p>

<p>The hyphens are optional since a user does not need top type them in, but they can be there if the user as done a copy &amp; paste.</p>

[{'answer_id': 25567, 'author': 'Steve M', 'author_id': 1693, 'author_profile': 'https://Stackoverflow.com/users/1693', 'pm_score': 0, 'selected': False, 'text': "<p>Why use Regex? If the groups are always split by a -, can't you use Split()?</p>\n"}, {'answer_id': 25569, 'author': 'Mark Glorie', 'author_id': 952, 'author_profile': 'https://Stackoverflow.com/users/952', 'pm_score': 0, 'selected': False, 'text': '<p>Sorry if this isn\'t what you intended, but your string always has the hyphen separating the groups then instead of using regex couldn\'t you use the String.Split() method?  </p>\n\n<pre><code>Dim stringArray As Array = someString.Split("-")\n</code></pre>\n'}, {'answer_id': 25591, 'author': 'aku', 'author_id': 1196, 'author_profile': 'https://Stackoverflow.com/users/1196', 'pm_score': 0, 'selected': False, 'text': '<p>You can use this pattern:</p>\n\n<pre><code>Regex.Split("H3Y5NC8E-TGA5B6SB-2NVAQ4E0", "([ABCDEFGHJKLMNPQRSTVXYZ0123456789]{8}+)-?")\n</code></pre>\n\n<p>But you will need to filter out empty strings from resulting array.\nCitation from <a href="http://msdn.microsoft.com/en-us/library/8yttk7sy.aspx" rel="nofollow noreferrer">MSDN</a>:</p>\n\n<blockquote>\n  <p>If multiple matches are adjacent to one another, an empty string is inserted into the array.</p>\n</blockquote>\n'}, {'answer_id': 25595, 'author': 'Rob Allen', 'author_id': 149, 'author_profile': 'https://Stackoverflow.com/users/149', 'pm_score': 0, 'selected': False, 'text': "<p>What are the defining characteristics of a valid block? We'd need to know that in order to really be helpful. </p>\n\n<p>My generic suggestion, validate the charset in a first step, then split and parse in a seperate method based on what you expect. If this is in a web site/app then you can use the ASP Regex validation on the front end then break it up on the back end. </p>\n"}, {'answer_id': 25597, 'author': 'Dale Ragan', 'author_id': 1117, 'author_profile': 'https://Stackoverflow.com/users/1117', 'pm_score': 2, 'selected': False, 'text': '<p>After reviewing your question and the answers given, I came up with this:</p>\n\n<pre><code>RegexOptions options = RegexOptions.None;\nRegex regex = new Regex(@"([ABCDEFGHJKLMNPQRSTVXYZ0123456789]{8})", options);\nstring input = @"H3Y5NC8E-TGA5B6SB-2NVAQ4E0";\n\nMatchCollection matches = regex.Matches(input);\nfor (int i = 0; i != matches.Count; ++i)\n{\n    string match = matches[i].Value;\n}\n</code></pre>\n\n<p>Since the "-" is optional, you don\'t need to include it.  I am not sure what you was using the {4} at the end for?  This will find the matches based on what you want, then using the MatchCollection you can access each match to rebuild the string.</p>\n'}, {'answer_id': 25601, 'author': 'Kibbee', 'author_id': 1862, 'author_profile': 'https://Stackoverflow.com/users/1862', 'pm_score': 0, 'selected': False, 'text': '<p>If you\'re just checking the value of the group, with group(i).value, then you will only get the last one.  However, if you want to enumerate over all the times that group was captured, use group(2).captures(i).value, as shown below.  </p>\n\n<pre><code>system.text.RegularExpressions.Regex.Match("H3Y5NC8E-TGA5B6SB-2NVAQ4E0","(([ABCDEFGHJKLMNPQRSTVXYZ0123456789]+)-?)*").Groups(2).Captures(i).Value\n</code></pre>\n'}, {'answer_id': 25602, 'author': 'aku', 'author_id': 1196, 'author_profile': 'https://Stackoverflow.com/users/1196', 'pm_score': 0, 'selected': False, 'text': '<p>Mike,</p>\n\n<p>You can use character set of your choice inside character group. All you need is to add "+" modifier to capture all groups. See my previous answer, just change [A-Z0-9] to whatever you need (i.e. [ABCDEFGHJKLMNPQRSTVXYZ0123456789])</p>\n'}, {'answer_id': 25622, 'author': 'Mike Thompson', 'author_id': 2754, 'author_profile': 'https://Stackoverflow.com/users/2754', 'pm_score': 3, 'selected': True, 'text': '<p>I have discovered the answer I was after. Here is my working code:</p>\n\n<pre><code>    static void Main(string[] args)\n    {\n        string pattern = @"^\\s*((?&lt;group&gt;[ABCDEFGHJKLMNPQRSTVXYZ0123456789]{8})-?){3}\\s*$";\n        string input = "H3Y5NC8E-TGA5B6SB-2NVAQ4E0";\n        Regex re = new Regex(pattern);\n        Match m = re.Match(input);\n\n        if (m.Success)\n            foreach (Capture c in m.Groups["group"].Captures)\n                Console.WriteLine(c.Value);\n    }\n</code></pre>\n'}, {'answer_id': 25909, 'author': 'Imran', 'author_id': 1897, 'author_profile': 'https://Stackoverflow.com/users/1897', 'pm_score': 3, 'selected': False, 'text': '<p>BTW, you can replace [ABCDEFGHJKLMNPQRSTVXYZ0123456789] character class with a more readable subtracted character class.</p>\n\n<pre><code>[[A-Z\\d]-[IOUW]]\n</code></pre>\n\n<p>If you just want to match 3 groups like that, why don\'t you use this pattern 3 times in your regex and just use captured 1, 2, 3 subgroups to form the new string?</p>\n\n<pre><code>([[A-Z\\d]-[IOUW]]){8}-([[A-Z\\d]-[IOUW]]){8}-([[A-Z\\d]-[IOUW]]){8}\n</code></pre>\n\n<p>In PHP I would return (I don\'t know .NET)</p>\n\n<pre><code>return "$1 $2 $3";\n</code></pre>\n'}]
['https://Stackoverflow.com/questions/25561', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/2754/']