What you posted isn't a replacement. It's using data from the CSV file to generate new text based on a template. Using a regular expression for replacement means you'd still have to parse the pattern for every line.
All Regex.Replace overloads search for a pattern in an input and replace it with the provided text. This means for every line, Regex.Replace would have to be called to parse the template and replace the placeholders with data from the CSV file.
Using a template engine
You should consider using a template engine like Scriban instead. You could create a template eg:
var template = Template.Parse(@"
<ul id='dataset'>
{{ for dataset in Datasets}}
<li>
<a href='{{ dataset.Url }}>{{ dataset.DatasetID }}</a>
</li>
{{ end }}
</ul>
");
And generate the entire file at once using the parsed data:
var result = template.Render(new { Datasets = setList });
You can reduce memory consumption by using File.ReadLines instead of File.ReadAllLines so that only a single line is read at a time.
record MySet(string DatasetID,string Url);
var setList=from line in File.ReadLines(csvPath)
let parts=line.Split(splitChar)
select new MySet(parts[0],parts[1]);
Another possibility is to use a library like CsvHelper to parse the CSV file directly into classes, handling headers, conversions, delimiters and more:
using var reader = new StreamReader("path\\to\\file.csv");
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture));
var sets= csv.GetRecords<MySet>();
var result = template.Render(new { Datasets = sets });
You can configure the delimiter, headers and more through configuration:
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false,
Delimiter = "|",
};
using var reader = new StreamReader("path\\to\\file.csv");
using var csv = new CsvReader(reader, configuration);