Экранирование строки в формате CSV
Мне нужно создать CSV файл из вывода webservice, а файл CSV использует цитированные строки с разделителем запятой. Я не могу изменить формат...
Итак, если у меня есть string
, он становится "string"
...
Если значение уже содержит кавычки, они заменяются двойными кавычками.
Например, a str"ing
становится "str""ing"
...
Однако в последнее время мой импорт завершился неудачей из-за следующих
- исходная строка ввода:
"","word1,word2,..."
- каждая отдельная цитата заменяется двойным результатом:
"""",""word1,word2,...""
- затем его префикс и суффикс с цитатой перед записью в файл CVS:
""""",""word1,word2,..."""
Как вы можете видеть, конечный результат таков:
""""",""word1,word2,..."""
который разбивает мой импорт (видит это как другое поле)...
Я думаю, что проблема заключается в начале ","
в исходной строке ввода.
Есть ли escape-последовательность CVS для этого сценария?
Update
Причина, по которой вышеуказанные разрывы связаны с файлом сопоставления BCP (утилита BCP используется для загрузки CSV файла в SQL db), который имеет терминатор, определенный как ","
. Поэтому вместо того, чтобы видеть 1 поле, он видит 2... Но я не могу изменить файл сопоставления...
Ответы
Ответ 1
Я использую этот код, и он всегда работал:
/// <summary>
/// Turn a string into a CSV cell output
/// </summary>
/// <param name="str">String to output</param>
/// <returns>The CSV cell formatted string</returns>
public static string StringToCSVCell(string str)
{
bool mustQuote = (str.Contains(",") || str.Contains("\"") || str.Contains("\r") || str.Contains("\n"));
if (mustQuote)
{
StringBuilder sb = new StringBuilder();
sb.Append("\"");
foreach (char nextChar in str)
{
sb.Append(nextChar);
if (nextChar == '"')
sb.Append("\"");
}
sb.Append("\"");
return sb.ToString();
}
return str;
}
Ответ 2
Основываясь на ответе Эд Байайца:
/// <summary>
/// Turn a string into a CSV cell output
/// </summary>
/// <param name="value">String to output</param>
/// <returns>The CSV cell formatted string</returns>
private string ConvertToCsvCell(string value)
{
var mustQuote = value.Any(x => x == ',' || x == '\"' || x == '\r' || x == '\n');
if (!mustQuote)
{
return value;
}
value = value.Replace("\"", "\"\"");
return string.Format("\"{0}\"", value);
}
Ответ 3
Моя копейка подумала:
String[] lines = new String[] { "\"\",\"word\",word,word2,1,34,5,2,\"details\"" };
for (int j = 0; j < lines.Length; j++)
{
String[] fields=lines[j].Split(',');
for (int i =0; i<fields.Length; i++)
{
if (fields[i].StartsWith("\"") && fields[i].EndsWith("\""))
{
char[] tmp = new char[fields[i].Length-2];
fields[i].CopyTo(1,tmp,0,fields[i].Length-2);
fields[i] =tmp.ToString();
fields[i] = "\""+fields[i].Replace("\"","\"\"")+"\"";
}
else
fields[i] = fields[i].Replace("\"","\"\"");
}
lines[j]=String.Join(",",fields);
}
Ответ 4
Основываясь на вкладе "Ed Bayiates" здесь полезного класса для создания документа csv:
/// <summary>
/// helpful class to build csv document
/// </summary>
public class CsvBuilder
{
/// <summary>
/// create the csv builder
/// </summary>
public CsvBuilder(char csvSeparator)
{
m_csvSeparator = csvSeparator;
}
/// <summary>
/// append a cell
/// </summary>
public void appendCell(string strCellValue)
{
if (m_nCurrentColumnIndex > 0) m_strBuilder.Append(m_csvSeparator);
bool mustQuote = (strCellValue.Contains(m_csvSeparator)
|| strCellValue.Contains('\"')
|| strCellValue.Contains('\r')
|| strCellValue.Contains('\n'));
if (mustQuote)
{
m_strBuilder.Append('\"');
foreach (char nextChar in strCellValue)
{
m_strBuilder.Append(nextChar);
if (nextChar == '"') m_strBuilder.Append('\"');
}
m_strBuilder.Append('\"');
}
else
{
m_strBuilder.Append(strCellValue);
}
m_nCurrentColumnIndex++;
}
/// <summary>
/// end of line, new line
/// </summary>
public void appendNewLine()
{
m_strBuilder.Append(Environment.NewLine);
m_nCurrentColumnIndex = 0;
}
/// <summary>
/// Create the CSV file
/// </summary>
/// <param name="path"></param>
public void save(string path )
{
File.WriteAllText(path, ToString());
}
public override string ToString()
{
return m_strBuilder.ToString();
}
private StringBuilder m_strBuilder = new StringBuilder();
private char m_csvSeparator;
private int m_nCurrentColumnIndex = 0;
}
Как использовать его:
void exportAsCsv( string strFileName )
{
CsvBuilder csvStringBuilder = new CsvBuilder(';');
csvStringBuilder.appendCell("#Header col 1 : Name");
csvStringBuilder.appendCell("col 2 : Value");
csvStringBuilder.appendNewLine();
foreach (Data data in m_dataSet)
{
csvStringBuilder.appendCell(data.getName());
csvStringBuilder.appendCell(data.getValue());
csvStringBuilder.appendNewLine();
}
csvStringBuilder.save(strFileName);
}
Ответ 5
первый шаг в анализе - удаление лишней добавленной "вокруг вашей строки". После этого вы сможете иметь дело со встроенными ", а также с.,
Ответ 6
После долгих обсуждений было принято решение о необходимости исправления формата утилиты импорта. Исключение строки было правильным (как указывали пользователи), но файл формата, в котором используется утилита импорта, был неправильным и вызывал у него прерывание импорта.
Спасибо всем и особое спасибо @dbt (голос)