using System.Text; namespace AIStudio.Assistants.BatchProcessing; /// /// Reads and writes the CSV files of the batch processing assistant. Fields /// are quoted according to RFC 4180, but the separator is a vertical bar, so /// that the files open nicely in spreadsheet applications regardless of the /// list separator of the user's locale. /// public static class BatchProcessingCsv { private const char SEPARATOR = '|'; public static string ToCsvRow(params string[] fields) => string.Join(SEPARATOR, fields.Select(ToCsvField)); /// /// Quotes one CSV field according to RFC 4180. /// private static string ToCsvField(string text) { if (string.IsNullOrEmpty(text)) return string.Empty; if (!text.Contains(SEPARATOR) && !text.Contains('"') && !text.Contains('\n') && !text.Contains('\r')) return text; return $"\"{text.Replace("\"", "\"\"")}\""; } /// /// Parses a CSV text which was written by . /// /// /// We parse the file ourselves instead of splitting lines, because quoted /// fields may contain the separator and line breaks. /// public static List> Parse(string content) { var rows = new List>(); var fields = new List(); var field = new StringBuilder(); var isQuoted = false; var hasContent = false; for (var index = 0; index < content.Length; index++) { var character = content[index]; if (isQuoted) { if (character is not '"') { field.Append(character); continue; } // A doubled quote is an escaped quote, everything else ends the quoted field: if (index + 1 < content.Length && content[index + 1] is '"') { field.Append('"'); index++; continue; } isQuoted = false; continue; } switch (character) { case '"': isQuoted = true; hasContent = true; break; case SEPARATOR: hasContent = true; EndField(); break; case '\r': break; case '\n': EndRow(); break; default: hasContent = true; field.Append(character); break; } } if (hasContent || field.Length > 0) EndRow(); return rows; void EndField() { fields.Add(field.ToString()); field.Clear(); } void EndRow() { EndField(); if (hasContent) rows.Add([..fields]); fields.Clear(); hasContent = false; } } }