AZJIO
Сейчас трудно вспомнить. Может что-то типа этого?

Код:
EnableExplicit

ImportC ""
	;pb_pcre_exec(*pcre, *extra, subject.p-utf8, length, startoffset, options, *ovector, ovecsize)
	pb_pcre_exec(*pcre, *extra, *subject, length, startoffset, options, *ovector, ovecsize)
EndImport

;==========================================================================================================================
Procedure.s RegexReplace1( Regex, Subject.s, Replacement.s )
	Protected Dim ovec(30) ; для MaximumReference = 9
	Protected GroupBegin, GroupEnd, GroupCount, GroupNumber, Offset
	Protected Result.s, Replacing.s
	Protected SubjectLength = StringByteLength(Subject,#PB_UTF8)
	Protected SubjectBuffer = AllocateMemory(SubjectLength+1,#PB_Memory_NoClear)
	PokeS(SubjectBuffer,Subject,-1,#PB_UTF8)
	While Offset < SubjectLength ; будем вызывать pb_pcre_exec, пока не закончится строка
    ; -1 - не нашли; 1 - совпадение; >1 - совпадение и группы
    GroupCount = pb_pcre_exec(PeekL(Regex),0,SubjectBuffer,SubjectLength,Offset,0,ovec(),ArraySize(ovec())) - 1
    If GroupCount >= 0
    	Replacing = Replacement
    	For GroupNumber=0 To 9 ; извлекаем группы и заменяем обратные ссылки
        GroupBegin = ovec(GroupNumber*2)
        GroupEnd = ovec(GroupNumber*2+1)
        ; Если группы нет, то в ovec будут нули и PeekS вернёт пустую строку
        Replacing = ReplaceString(Replacing,"\"+Str(GroupNumber),PeekS(SubjectBuffer+GroupBegin,GroupEnd-GroupBegin,#PB_UTF8))
    	Next
    	; ovec(0) - начало совпадения, ovec(1) - конец совпадения
    	; Result + часть строки между началом и первым совпадением или между двумя совпадениями + результат подстановки групп
    	Result + PeekS(SubjectBuffer+Offset,ovec(0)-Offset,#PB_UTF8) + Replacing
    	Offset = ovec(1)
    Else ; больше ничего не найдено или вообще ничего не найдено
    	; Result + остаток строки
    	Result + PeekS(SubjectBuffer+Offset,SubjectLength-Offset,#PB_UTF8)
    	Break
    EndIf
	Wend
	FreeMemory(SubjectBuffer)
	ProcedureReturn Result
EndProcedure

;==========================================================================================================================
Procedure.s RegexReplace2( Regex, Subject.s, Replacement.s )
	Protected GroupNumber, GroupCount, MatchPos, Offset=1
	Protected Replacing.s, Result.s
	If ExamineRegularExpression(Regex,Subject)
    While NextRegularExpressionMatch(Regex)
    	MatchPos = RegularExpressionMatchPosition(Regex)
    	Replacing = ReplaceString(Replacement,"\0",RegularExpressionMatchString(Regex)) ; обратная ссылка \0
    	GroupCount = CountRegularExpressionGroups(Regex)
    	If GroupCount>9 : GroupCount=9 : EndIf ; только обратные ссылки \1 .. \9
    	For GroupNumber=1 To GroupCount
        Replacing = ReplaceString(Replacing,"\"+Str(GroupNumber),RegularExpressionGroup(Regex,GroupNumber))
    	Next
    	For GroupNumber=GroupCount+1 To 9 ; отсутствующие группы на пустые строки
        Replacing = ReplaceString(Replacing,"\"+Str(GroupNumber),"")
    	Next
    	; Result + часть строки между началом и первым совпадением или между двумя совпадениями + результат подстановки групп
    	Result + Mid(Subject,Offset,MatchPos-Offset) + Replacing
    	Offset = MatchPos+RegularExpressionMatchLength(Regex)
    Wend
    ProcedureReturn Result + Mid(Subject,Offset) ; Result + остаток строки
	EndIf
	ProcedureReturn Subject ; без изменений
EndProcedure

;==========================================================================================================================
Procedure.s RegexReplace3( Regex, Subject.s, Replacement.s )
	Protected Dim ovec(30) ; для MaximumReference = 9
	Protected Dim Groups.s(9)
	Protected GroupBegin, GroupEnd, GroupCount, GroupNumber, Offset
	Protected RPos, ROffset, RLen=Len(Replacement)
	Protected Result.s, Replacing.s
	Protected SubjectLength = StringByteLength(Subject,#PB_UTF8)
	Protected SubjectBuffer = AllocateMemory(SubjectLength+1,#PB_Memory_NoClear)
	PokeS(SubjectBuffer,Subject,-1,#PB_UTF8)
	While Offset < SubjectLength ; будем вызывать pb_pcre_exec, пока не закончится строка
    ; -1 - не нашли; 1 - совпадение; >1 - совпадение и группы
    GroupCount = pb_pcre_exec(PeekL(Regex),0,SubjectBuffer,SubjectLength,Offset,0,ovec(),ArraySize(ovec())) - 1
    If GroupCount >= 0
    	For GroupNumber=0 To 9
        ; Извлекаем строку совпадения (\0) и группы (\1..\9).
        ; Если группы нет, то в ovec будут нули и PeekS вернёт пустую строку
        GroupBegin = ovec(GroupNumber*2)
        GroupEnd = ovec(GroupNumber*2+1)
        Groups(GroupNumber) = PeekS(SubjectBuffer+GroupBegin,GroupEnd-GroupBegin,#PB_UTF8)
    	Next
    	Replacing = ""
    	RPos = 1
    	ROffset = 1
    	While RPos < RLen ; последний символ не обрабатываем
        Select Mid(Replacement,RPos,2)
        	Case "\\"
            RPos + 2
        	Case "\0","\1","\2","\3","\4","\5","\6","\7","\8","\9"
            GroupNumber = Val(Mid(Replacement,RPos+1,1))
            Replacing + Mid(Replacement,ROffset,RPos-ROffset) + Groups(GroupNumber)
            RPos + 2
            ROffset = RPos
        	Default
            RPos + 1
        EndSelect
    	Wend
    	; ovec(0) - начало совпадения, ovec(1) - конец совпадения
    	; Result + часть строки между началом и первым совпадением или между двумя совпадениями
    	;        + результат подстановки групп
    	;        + остаток строки замены
    	Result + PeekS(SubjectBuffer+Offset,ovec(0)-Offset,#PB_UTF8) + Replacing + Mid(Replacement,ROffset)
    	Offset = ovec(1)
    Else ; больше ничего не найдено или вообще ничего не найдено
    	; Result + остаток строки
    	Result + PeekS(SubjectBuffer+Offset,SubjectLength-Offset,#PB_UTF8)
    	Break
    EndIf
	Wend
	FreeMemory(SubjectBuffer)
	ProcedureReturn Result
EndProcedure

;==========================================================================================================================
Procedure.s RegexReplace4( Regex, Subject.s, Replacement.s )
	Protected MatchPos, GroupNumber, GroupCount, Offset=1
	Protected RPos, ROffset, RLen=Len(Replacement)
	Protected Replacing.s, Result.s, Spec.s
	If ExamineRegularExpression(Regex,Subject)
    While NextRegularExpressionMatch(Regex)
    	MatchPos = RegularExpressionMatchPosition(Regex)
    	GroupCount = CountRegularExpressionGroups(Regex)
    	Replacing = ""
    	RPos = 1
    	ROffset = 1
    	While RPos < RLen ; последний символ не обрабатываем
        Spec = Mid(Replacement,RPos,2)
        Select Spec
        	Case "\\"
            RPos + 2
        	Case "\0"
            Replacing + Mid(Replacement,ROffset,RPos-ROffset) + RegularExpressionMatchString(Regex)
            RPos + 2
            ROffset = RPos
        	Case "\1","\2","\3","\4","\5","\6","\7","\8","\9"
            GroupNumber = Val(Mid(Spec,2))
            Replacing + Mid(Replacement,ROffset,RPos-ROffset)
            If GroupNumber <= GroupCount ; существующая группа, иначе - пусто
            	Replacing + RegularExpressionGroup(Regex,GroupNumber)
            EndIf
            RPos + 2
            ROffset = RPos
        	Default
            RPos + 1
        EndSelect
    	Wend
    	; Result + часть строки между началом и первым совпадением или между двумя совпадениями
    	;        + результат подстановки групп
    	;        + остаток строки замены
    	Result + Mid(Subject,Offset,MatchPos-Offset) + Replacing + Mid(Replacement,ROffset)
    	Offset = MatchPos+RegularExpressionMatchLength(Regex)
    Wend
    ProcedureReturn Result + Mid(Subject,Offset) ; Result + остаток строки
	EndIf
	ProcedureReturn Subject ; без изменений
EndProcedure

;==========================================================================================================================

В новых версиях PB у меня внезапно начали глючить функции на основе pb_pcre_exec.
Но, слава богу, появилась возможность работать с группами.