const parseCsvLine = (line) => { const result = []; // Regex handles fields enclosed in quotes (with escaped quotes like "") or unquoted fields const csvRegex = /(?:"([^"]*(?:""[^"]*)*)"|([^",]*))(?:,|$)/g; let match; while ((match = csvRegex.exec(line)) !== null) { // Group 1 is for quoted fields, Group 2 for unquoted fields // If a field was quoted, it's in match[1] and needs "" -> " unescaping // Otherwise, it's in match[2] const value = match[1] !== undefined ? match[1].replace(/""/g, '"') : match[2]; result.push(value); if (match[0].endsWith(',')) { // If the last character was a comma, it means another field is expected // This ensures trailing empty fields are captured. continue; } else if (match[0].length === line.length) { // If we've consumed the entire line, break break; } } return result; }; console.log(parseCsvLine('value1,"value2 with, comma",value3')); // Expected: ["value1", "value2 with, comma", "value3"] console.log(parseCsvLine('field_a,"field_b, with ""quotes"" inside",field_c')); // Expected: ["field_a", "field_b, with \"quotes\" inside", "field_c"] console.log(parseCsvLine('one,two,,four,')); // Expected: ["one", "two", "", "four", ""] console.log(parseCsvLine('"only one field"')); // Expected: ["only one field"]