Skip to content

Commit 91151c5

Browse files
committed
fix(csv): properly escape delimiters, quotes and newlines in DataTable.ToCsv output
1 parent 096cd16 commit 91151c5

File tree

1 file changed

+14
-10
lines changed

1 file changed

+14
-10
lines changed

SharpHelpers/SharpHelpers/DataTableHelper.cs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,18 +63,22 @@ public static DataTable SetColumnsOrder(this DataTable table, string[] columnNam
6363
public static string ToCsv(this DataTable table, string delimiter = ",")
6464
{
6565
if (table == null) throw new ArgumentNullException(nameof(table));
66-
67-
var csv = new List<string>();
68-
var headers = string.Join(delimiter, table.Columns.Cast<DataColumn>().Select(c => c.ColumnName));
69-
csv.Add(headers);
70-
71-
foreach (DataRow row in table.Rows)
66+
string Escape(string s)
7267
{
73-
var line = string.Join(delimiter, row.ItemArray.Select(field => field?.ToString()));
74-
csv.Add(line);
68+
if (s == null) return "";
69+
bool needQuotes = s.Contains(delimiter) || s.Contains('"') || s.Contains('\n') || s.Contains('\r');
70+
if (s.Contains('"')) s = s.Replace("\"", "\"\"");
71+
return needQuotes ? $"\"{s}\"" : s;
7572
}
76-
return string.Join(Environment.NewLine, csv);
77-
}
73+
74+
var lines = new List<string>(table.Rows.Count + 1)
75+
{
76+
string.Join(delimiter, table.Columns.Cast<DataColumn>().Select(c => Escape(c.ColumnName)))
77+
};
78+
foreach (DataRow row in table.Rows)
79+
lines.Add(string.Join(delimiter, row.ItemArray.Select(v => Escape(v?.ToString()))));
80+
return string.Join(Environment.NewLine, lines);
81+
}
7882

7983
/// <summary>
8084
/// Adds a new column to the DataTable with the specified default value.

0 commit comments

Comments
 (0)