Avatar billede simsen Mester
29. december 2011 - 13:37 Der er 9 kommentarer og
1 løsning

Read/write til csv fil

Hej,

Jeg har fundet en klasse på nettet, som skulle gøre det nemt at skrive/læst til/fra en csv fil. I den forbindelse har jeg nogle problemer, som jeg håber I kan hjælpe mig med;

1) Når jeg skriver til en fil, laver den æ ø og å om til mærkelige tegn.
2) Når jeg læser fra en csv fil, hvor der pænt står æ ø og å så indlæser den disse til mærkelige tegn.

Hvordan ændrer jeg det så den pænt skriver æ ø og å og hvordan ændrer jeg det, så den også læser æ ø og å som æ ø og å?

3) Når jeg åbner en csv fil i Excel 2010 (aner ikke om dette også er gældende i ældre udgiver) (som jeg har lavet som komma separeret fil), skriver den det i en lang streng. Altså Excel deler den ikke op i kolonner.

Hvordan får jeg Excel til at indlæse csv filen så den opdeler i kolonner og ikke tager en række som en lang streng?

Klassen som jeg bruger:

public class CsvRow : List<string>
    {
        public string LineText { get; set; }
    }

    public class CsvFileWriter : StreamWriter
    {
        public CsvFileWriter(Stream stream)
            : base(stream)
        {
        }

        public CsvFileWriter(string filename)
            : base(filename)
        {
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="row"></param>
        public void WriteRow(CsvRow row)
        {
            StringBuilder builder = new StringBuilder();
            foreach (string value in row)
            {
                // Add separator if this isn't the first value
                if (builder.Length > 0)
                    builder.Append(',');

                if (value.IndexOfAny(new char[] { '"', ',' }) != -1)
                {
                    // Special handling for values that contain comma or quote
                    // Enclose in quotes and double up any double quotes
                    builder.AppendFormat("\"{0}\"", value.Replace("\"", "\"\""));
                }
                else builder.Append(value);
            }
            row.LineText = builder.ToString();
            WriteLine(row.LineText);
        }
    }

    public class CsvFileReader : StreamReader
    {
        public CsvFileReader(Stream stream)
            : base(stream)
        {
        }

        public CsvFileReader(string filename)
            : base(filename)
        {
        }

        /// <summary>
        /// Reads a row of data from a CSV file
        /// </summary>
        /// <param name="row"></param>
        /// <returns></returns>
        public bool ReadRow(CsvRow row)
        {
            row.LineText = ReadLine();
            if (String.IsNullOrEmpty(row.LineText))
                return false;

            int pos = 0;
            int rows = 0;

            while (pos < row.LineText.Length)
            {
                string value;

                // Special handling for quoted field
                if (row.LineText[pos] == '"')
                {
                    // Skip initial quote
                    pos++;

                    // Parse quoted value
                    int start = pos;
                    while (pos < row.LineText.Length)
                    {
                        // Test for quote character
                        if (row.LineText[pos] == '"')
                        {
                            // Found one
                            pos++;

                            // If two quotes together, keep one
                            // Otherwise, indicates end of value
                            if (pos >= row.LineText.Length || row.LineText[pos] != '"')
                            {
                                pos--;
                                break;
                            }
                        }
                        pos++;
                    }
                    value = row.LineText.Substring(start, pos - start);
                    value = value.Replace("\"\"", "\"");
                }
                else
                {
                    // Parse unquoted value
                    int start = pos;
                    while (pos < row.LineText.Length && row.LineText[pos] != ',')
                        pos++;
                    value = row.LineText.Substring(start, pos - start);
                }

                // Add field to list
                if (rows < row.Count)
                    row[rows] = value;
                else
                    row.Add(value);
                rows++;

                // Eat up to and including next comma
                while (pos < row.LineText.Length && row.LineText[pos] != ',')
                    pos++;
                if (pos < row.LineText.Length)
                    pos++;
            }
            // Delete any unused items
            while (row.Count > rows)
                row.RemoveAt(rows);

            // Return true if any columns read
            return (row.Count > 0);
        }
    }

Mvh.
simsen :-)
Avatar billede simsen Mester
29. december 2011 - 14:08 #1
Jeg har fundet løsningen til æ ø og å problematikken....selvom den gør mig en kende nervøs....

For at skrive til en fil, skal jeg i klassen skrive følgende:
public CsvFileWriter(string filename)
            : base(filename, false, Encoding.Unicode)
        {
        }

Altså med Encoding.Unicode

for at læse en fil skal jeg i klassen skrive følgende:
public CsvFileReader(string filename)
            : base(filename, Encoding.UTF7)
        {
        }

Altså med Encoding.UTF7

Og ja, det gør mig nervøs at det er to forskellige - nogen der kan give mig en forklaring?

Og så mangler jeg stadig at Excel læser den kommaseparerede fil som én lang streng i stedet for at dele den op?
Avatar billede janus_007 Nybegynder
29. december 2011 - 17:59 #2
Hej simsen

Ved da ikke helt hvor du lige har fundet det eksempel, men tilsyneladende har manden da ikke lært at kode.

Det er jo næsten lige til www.wtfcode.net hehe...


Når opgaven er så simpel som csv, så er det meget nemmere selv at klare.


class Program
    {
        static void Main(string[] args)
        {
            const string seperator = ";";
            var input = new List<string>{"En", "ræ,kke", "jeg", "gerne", "vil", "have", "indsat", "som", "csv"};

            input = input.Select(x => x.Replace("\"", "\"\""))
                    .Select(x => x.Contains(',') ? x.Replace(",", "\",\"") : x)
                    .ToList();
                   
            using (var writer = new System.IO.StreamWriter("C:\\somefilename.csv", true, Encoding.UTF8))
            {
               
                writer.WriteLine(string.Join(seperator, input));
            }
        }
    }



Som du kan se har jeg brugt UTF8 :)

Jeg formoder du selv kan omskrive til en funktion som modtager input som input :)

Stil endelig spørgsmål, hvis der er noget.
Avatar billede janus_007 Nybegynder
29. december 2011 - 18:01 #3
Hov... forresten... Excel opfatter ; som seperator og ikke , som standard. Ellers skal du importere i Excel istedet, her kan du selv bestemme hvilke seperatorer du bruger mv.
Avatar billede Slettet bruger
30. december 2011 - 20:57 #4
Excel bruger de "regionale indstillinger" windows kører med til at bestemme hvad der fungerer som "List separator" som default (og på dansk er det semikolon medmindre man har ændret det til noget andet). Det skal jo ikke være for nemt. :)
Avatar billede Slettet bruger
30. december 2011 - 21:43 #5
Hvis du skriver en fil med unicode encoding, burde du også kunne læse den igen med unicode. Ellers må der være noget andet der er gået galt.

Og lidt om nogle typiske encodings:

Unicode bruger 32 bit (4 byte) per tegn. UTF-7 er en forældet encoding der næppe bruges mere. Du kan med fordel bruge UTF-8, som janus nævner, som encoding, både når du skriver og læser en fil. Den fylder aldrig mere end hvis du havde gemt den som unicode, og i de fleste tilfælde vil den fylde 1/4 af hvad det ville have kostet at gemme som unicode.

Windows bruger som standard windows-1252 som encoding (f.eks. på en dansk/engelsk windows). Så hvis du ikke har specificeret en encoding, er det i dette format filen bliver gemt i. Denne encoding kan ikke bruges til at gemme alle unicode tegn:
http://en.wikipedia.org/wiki/Windows-1252

En anden meget brugt encoding er ISO 8859-1, men den kan heller ikke bruges til at gemme alle unicode tegn, og den er ikke helt kompatibel med windows-1252. Men de gemmer begge to de danske bogstaver på samme måde, så man kan nemt forveksle de to formatter.
http://en.wikipedia.org/wiki/ISO/IEC_8859-1
Avatar billede simsen Mester
02. januar 2012 - 12:43 #6
Hej Janus,

Jeg har forsøgt mig med din og skriver jeg den i selve filen (hvor jeg skal bruge den virker den);

const string seperator = ";";

            using (var writer = new System.IO.StreamWriter("WriteTest.csv", true, Encoding.UTF8))
            {
                string[] arr5 = new string[5];
                for (int i = 0; i < 100; i++)
                {
                    for (int j = 0; j < 5; j++)
                    {
                        string myString = string.Format("Column,ÆøÅ{0}{1}{2}", j, "Række ", i);

                        arr5[j] = myString;                 
                    }

                    arr5.Select(x => x.Replace("\"", "\"\"")).Select(x => x.Contains(',') ? x.Replace(",", "\",\"") : x).ToList();
                    writer.WriteLine(string.Join(seperator, arr5));
                }               
            }

MEN når jeg forsøger at lave en klasse og kopierer din kode ind, får jeg følgende fejl;

Error    3    'System.Array' does not contain a definition for 'Select' and no extension method 'Select' accepting a first argument of type 'System.Array' could be found (are you missing a using directive or an assembly reference?)    C:\1Projekter\TimeReg\Kyborg.Util\ReadWriteCSV.cs    47    26    Kyborg.Util

og
Error    5    Argument '1': cannot convert from 'char' to 'string'    C:\1Projekter\TimeReg\Kyborg.Util\ReadWriteCSV.cs    48    50    Kyborg.Util

og
Error    4    The best overloaded method match for 'string.Join(string, string[])' has some invalid arguments    C:\1Projekter\TimeReg\Kyborg.Util\ReadWriteCSV.cs    48    38    Kyborg.Util


Det jeg helt nøjagtigt forsøger er at lave en klasse med 2 metoder. En hvor jeg smider en ArrayList ind og den så skriver en csv fil efter denne og en hvor jeg smider ind DataTable ind og så den laver en csv fil ud fra denne.

Mine metoder;
public bool WriteCsvFromDataTable(DataTable table, string fileName, char seperator)
        {
            bool isSucceded = false;
            using (var writer = new System.IO.StreamWriter(fileName, true, Encoding.UTF8))
            {
                string[] myArray = new string[table.Columns.Count];

                for (int i = 0; i < table.Rows.Count; i++)
                {
                    for (int j = 0; j < table.Columns.Count; i++)
                    {
                        string myString = table.Rows[i][j].ToString();
                        myArray[j] = table.Rows[i][j].ToString();
                    }

                    myArray.Select(x => x.Replace("\"", "\"\"")).Select(x => x.Contains(',') ? x.Replace(",", "\",\"") : x).ToList();
                    writer.WriteLine(string.Join(seperator, myArray));
                }

                isSucceded = true;
            }

            return isSucceded;
        }
public bool WriteCsvFromArrayList(ArrayList list, string fileName, char seperator)
        {
            bool isSucceded = false;

            using (var writer = new System.IO.StreamWriter(fileName, true, Encoding.UTF8))
            {
                string[] arr5 = new string[5];
                for (int i = 0; i < list.Count; i++)
                {
                    for (int j = 0; j < 5; j++)
                    {
                        string myString = string.Format("Column,ÆøÅ{0}{1}{2}", j, "Række ", i);

                        arr5[j] = myString;
                    }

                    arr5.Select(x => x.Replace("\"", "\"\"")).Select(x => x.Contains(',') ? x.Replace(",", "\",\"") : x).ToList();
                    writer.WriteLine(string.Join(seperator, arr5));
                }
            }


            return isSucceded;
        }

Som du kan se, har jeg ikke lavet noget om i WriteCsvFromArrayList metoden. Men alligevel kommer den med ovennævnte fejl.....Og jeg har ikke kunnet finde en using direktiv, der så finder arr5.Select.

Kan du fortælle mig, hvad jeg gør forkert? (lige nu har jeg jo slet ikke ændret noget i WriteCsvFromArrayList metoden, kun kopieret direkte.
Avatar billede simsen Mester
02. januar 2012 - 12:45 #7
Og så et ps spørgsmål - hvordan får du dine kodeafsnit fint markeret med blåt.....troede kun det var fed, kursiv og understreget BB-code-tags der var tilladte?
Avatar billede simsen Mester
02. januar 2012 - 15:06 #8
Fejlene bestod i at jeg skulle have

using System.Linq; med og at caste seperator til en string.

Smid et svar Janus og du får dine points. Og tak for hjælpen :-)

tolamaps

Du har ret, jeg har også læst mig til, at hvis man ændrer Internationale/sproglige indstillinger's Listeseparator fra ; til , så ville Excel bruge komma til at liste i kolonner. Dette prøvede jeg uden nogen som helst virkning.

Tog jeg derimod og lavede en import i Excel og valgte , som separator satte den kolonnerne korrekt i stedet for at smide dem i én kolonne.
Avatar billede janus_007 Nybegynder
02. januar 2012 - 22:30 #9
Tip top simsen, glad for jeg kunne hjælpe :)

Tags: http://www.eksperten.dk/guide/1325

Og så har Eksperten fået fixet (code) (\code), udskift () med []
Avatar billede arne_v Ekspert
03. januar 2012 - 01:24 #10
Det man i Windows og .NET kalder Unicode encoding er er ikke UTF-32 men UTF-16.

(og UTF-8 er langt bedre til filer end baade UTF-16 og UTF-32)
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
IT-kurser om Microsoft 365, sikkerhed, personlig vækst, udvikling, digital markedsføring, grafisk design, SAP og forretningsanalyse.

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester