Excuse me for two points:
I propose solutions that aren’t entirely Regex based. I know, I read that you need pure Regex solutions. But I went into the interesting problem and I rapidly concluded that usage of regexes for this problem is overcomplicating it. I didn’t fell able to answer with pure Regex solutions. I found the following ones, and I show them; maybe, they could give you ideas.
I dont know C# or .NET, only Python. As regexes are nearly the same in all languages, I thought I was going to answer with just regexes, that’s why I began to search about the problem. Now, I show my solutions in Python all the same because I think that anyway it’s easy to understand.
I think it’s very difficult to capture all the occurences of letters that you want in a text by means of a unique regex, because finding several letters in several lines seems to me a problem of finding nested matches in matches (maybe am I not enough skilled in regexes).
So I thought better to search primarily all the occurences of letters in all lines and to put them in a list, and next to select the whished occurences by slicing in the list.
For the search of letters in a line, a regex seemed OK to me first. SO the solution with function selectRE().
Afterwarrds, I realized that selecting the letters in a line is the same as slicing a line at convenient indexes and that’s the same as slicng a list. Hence the function select().
I give the two solutions together, so the equality of the two results of the two functions can be verified.
import re
def selectRE(a,which_chars,b,x,which_lines,y,ch):
ch = ch[:-1] if ch[1]=='\n' else ch # to obtain an exact number of lines
NL = ch.count('\n') +1 # number of lines
def pat(a,which_chars,b):
if which_chars=='to':
print repr(('.{'+str(a-1)+'}' if a else '') + '(.{'+str(b-a+1)+'}).*(?:\n|$)')
return re.compile(('.{'+str(a-1)+'}' if a else '') + '(.{'+str(b-a+1)+'}).*(?:\n|$)')
elif which_chars=='before':
print repr('.*(.{'+str(a)+'})'+('.{'+str(b)+'}' if b else '')+'(?:\n|$)')
return re.compile('.*(.{'+str(a)+'})'+('.{'+str(b)+'}' if b else '')+'(?:\n|$)')
elif which_chars=='after':
print repr(('.{'+str(b)+'}' if b else '')+'(.{'+str(a)+'}).*(?:\n|$)')
return re.compile(('.{'+str(b)+'}' if b else '')+'(.{'+str(a)+'}).*(?:\n|$)')
if which_lines=='to' : x = x-1
elif which_lines=='before': x,y = NL-x-y,NL-y
elif which_lines=='after' : x,y = y,y+x
return pat(a,which_chars,b).findall(ch)[x:y]
def select(a,which_chars,b,x,which_lines,y,ch):
ch = ch[:-1] if ch[1]=='\n' else ch # to obtain an exact number of lines
NL = ch.count('\n') +1 # number of lines
if which_chars=='to' : a = a-1
elif which_chars=='after' : a,b = b,a+b
if which_lines=='to' : x = x-1
elif which_lines=='before': x,y = NL-x-y,NL-y
elif which_lines=='after' : x,y = y,y+x
return [ line[len(line)-a-b:len(line)-b] if which_chars=='before' else line[a:b]
for i,line in enumerate(ch.splitlines()) if x<=i<y ]
ch = '''line1 blah 1
line2 blah 2
line3 blah 3
line4 blah 4
line5 blah 5
line6 blah 6
'''
print ch,'\n'
print 'Characters 3-6 of lines 3-5. A total of 3 matches.'
print selectRE(3,'to',6,3,'to',5,ch)
print select(3,'to',6,3,'to',5,ch)
print
print 'Characters 1-5 of lines 4-5. A total of 2 matches.'
print selectRE(1,'to',5,4,'to',5,ch)
print select(1,'to',5,4,'to',5,ch)
print
print '7 characters before the last 3 chars of lines 2-6. A total of 5 matches.'
print selectRE(7,'before',3,2,'to',6,ch)
print select(7,'before',3,2,'to',6,ch)
print
print '6 characters before the 2 last characters of 3 lines before the 3 last lines.'
print selectRE(6,'before',2,3,'before',3,ch)
print select(6,'before',2,3,'before',3,ch)
print
print '4 last characters of 2 lines before 1 last line. A total of 2 matches.'
print selectRE(4,'before',0,2,'before',1,ch)
print select(4,'before',0,2,'before',1,ch)
print
print 'last 1 character of 4 last lines. A total of 2 matches.'
print selectRE(1,'before',0,4,'before',0,ch)
print select(1,'before',0,4,'before',0,ch)
print
print '7 characters before the last 3 chars of 3 lines after the 2 first lines. A total of 5 matches.'
print selectRE(7,'before',3,3,'after',2,ch)
print select(7,'before',3,3,'after',2,ch)
print
print '5 characters before the 3 last chars of the 5 first lines'
print selectRE(5,'before',3,5,'after',0,ch)
print select(5,'before',3,5,'after',0,ch)
print
print 'Characters 3-6 of the 4 first lines'
print selectRE(3,'to',6,4,'after',0,ch)
print select(3,'to',6,4,'after',0,ch)
print
print '9 characters after the 2 first chars of the 3 lines after the 1 first line'
print selectRE(9,'after',2,3,'after',1,ch)
print select(9,'after',2,3,'after',1,ch)
result
line1 blah 1
line2 blah 2
line3 blah 3
line4 blah 4
line5 blah 5
line6 blah 6
Characters 3-6 of lines 3-5. A total of 3 matches.
'.{2}(.{4}).*(?:\n|$)'
['ne3 ', 'ne4 ', 'ne5 ']
['ne3 ', 'ne4 ', 'ne5 ']
Characters 1-5 of lines 4-5. A total of 2 matches.
'.{0}(.{5}).*(?:\n|$)'
['line4', 'line5']
['line4', 'line5']
7 characters before the last 3 chars of lines 2-6. A total of 5 matches.
'.*(.{7}).{3}(?:\n|$)'
['ne2 bla', 'ne3 bla', 'ne4 bla', 'ne5 bla', 'ne6 bla']
['ne2 bla', 'ne3 bla', 'ne4 bla', 'ne5 bla', 'ne6 bla']
6 characters before the 2 last characters of 3 lines before the 3 last lines.
'.*(.{6}).{2}(?:\n|$)'
['2 blah', '3 blah', '4 blah']
['2 blah', '3 blah', '4 blah']
4 last characters of 2 lines before 1 last line. A total of 2 matches.
'.*(.{4})(?:\n|$)'
['ah 5', 'ah 6']
['ah 5', 'ah 6']
last 1 character of 4 last lines. A total of 2 matches.
'.*(.{1})(?:\n|$)'
['4', '5', '6']
['4', '5', '6']
7 characters before the last 3 chars of 3 lines after the 2 first lines. A total of 5 matches.
'.*(.{7}).{3}(?:\n|$)'
['ne3 bla', 'ne4 bla', 'ne5 bla']
['ne3 bla', 'ne4 bla', 'ne5 bla']
5 characters before the 3 last chars of the 5 first lines
'.*(.{5}).{3}(?:\n|$)'
['1 bla', '2 bla', '3 bla', '4 bla', '5 bla']
['1 bla', '2 bla', '3 bla', '4 bla', '5 bla']
Characters 3-6 of the 4 first lines
'.{2}(.{4}).*(?:\n|$)'
['ne1 ', 'ne2 ', 'ne3 ', 'ne4 ']
['ne1 ', 'ne2 ', 'ne3 ', 'ne4 ']
9 characters after the 2 first chars of the 3 lines after the 1 first line
'.{2}(.{9}).*(?:\n|$)'
['ne2 blah ', 'ne3 blah ', 'ne4 blah ']
['ne2 blah ', 'ne3 blah ', 'ne4 blah ']
And now I will study the tricky solutions of Tim Pietzcker