web-dev-qa-db-fra.com

échapper une chaîne délicate au format CSV

Je dois créer un fichier CSV à partir de la sortie du service Web et le fichier CSV utilise des chaînes entre guillemets avec séparateur de virgules. je ne peux pas changer le format ...

Donc si j'ai un string ça devient un "string"... Si la valeur a déjà des guillemets, ils sont remplacés par des guillemets doubles. Par exemple, un str"ing devient "str""ing"...

Cependant, récemment, mon importation a échoué en raison des éléments suivants

  • la chaîne d'entrée d'origine est: "","Word1,Word2,..."
  • chaque devis est remplacé par un double, ce qui donne: """",""Word1,Word2,...""
  • puis son préfixé et suffixé avec guillemet avant écrit dans le fichier CVS: """"",""Word1,Word2,..."""

Comme vous pouvez le voir, le résultat final est le suivant:

""""",""Word1,Word2,..."""

ce qui rompt mon importation (c'est le voir comme un autre champ) ... Je pense que le problème est l'apparence de "," dans la chaîne d'entrée d'origine.

Existe-t-il une séquence d'échappement CVS pour ce scénario?

Mettre à jour

La raison pour laquelle les ruptures ci-dessus est due au fichier de mappage BCP (l'utilitaire BCP est utilisé pour charger le fichier CSV dans SQL db) qui a un terminateur défini comme ",". Donc, au lieu de voir 1 champ, il en voit 2 ... Mais je ne peux pas changer le fichier de mappage ...

33
zam6ak

J'utilise ce code et cela a toujours fonctionné:

/// <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;
}
84
Ed Bayiates

D'après la réponse d'Ed Bayiates:

/// <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);
}
8
Lenin

Mon sou pensait:

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);

}

2
BugFinder

Basé sur la contribution de "Ed Bayiates" voici une classe utile pour construire un document 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;

}

Comment l'utiliser:

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);
}
2
Patrice I

la première étape de l'analyse consiste à supprimer les "" supplémentaires ajoutés autour de votre chaîne.

1
Mike

Après de longues délibérations, il a été décidé que le format de l'utilitaire d'importation devait être corrigé. L'échappement de la chaîne était correct (comme les utilisateurs l'ont indiqué) mais le fichier de format utilisé par l'utilitaire d'importation était incorrect et provoquait l'interruption de l'importation.

Merci à tous et merci à @dbt (vote en hausse)

0
zam6ak