You have too many unnecessary groups. Also, since you actually need 2 groups to match the same type of value, you may use a named capturing group, and grab all your required matches with a regex like
actual (?:lat|lon) (?<val>\d+\.\d{1,6})|(?<val>\d+\.\d{1,6}) (?:lat|lon)
See the regex demo. If you use a RegexOptions.ExplicitCapture
flag, you can use capturing groups as non-capturing ones (only the named capturing groups will keep their submatches). See the C# demo:
var s = "lat range: 49.000000 to 50.000000 actual lat 49.212059 lon range: 16.000000 to 17.000000 actual lon 16.626276";
var pattern = @"actual (lat|lon) (?<val>\d+\.\d{1,6})|(?<val>\d+\.\d{1,6}) (lat|lon)";
var results = Regex.Matches(s, pattern)
.Cast<Match>()
.Select(m => m.Groups["val"].Value)
.ToList();
Console.WriteLine(string.Join("\n", results));
// => 49.212059
// 16.626276
If you put the (lon|lat)
into a named capturing group, you will be able to get a dictionary as a result:
var pattern = @"actual (?<type>lat|lon) (?<val>\d+\.\d{1,6})|(?<val>\d+\.\d{1,6}) (?<type>lat|lon)";
var results = Regex.Matches(s, pattern)
.Cast<Match>()
.ToDictionary(
m => m.Groups["type"].Value,
m => m.Groups["val"].Value);
foreach (var kv in results)
Console.WriteLine("'{0}': '{1}'", kv.Key, kv.Value);
// => 'lat': '49.212059'
// 'lon': '16.626276'
See another C# demo.