extras:CSVFile:CSVFileValuesParser: improved the whole csv thing with proper class...
[sdk] / extras / CSVFile.ec
1 #include <stdarg.h>
2
3 public import "ecere"
4
5 public struct CSVFileParameters
6 {
7    char fieldSeparator;
8    char valueQuotes;
9    int expectedFieldCount;
10    bool tolerateNewLineInValues;
11    //bool checkNulls;
12    //bool checkCurlies;
13 };
14
15 CSVFileParameters classicParameters = { ',', '\"', 0, false };
16
17 public struct CSVFileState
18 {
19    uint lineNum;
20    uint charNum;
21    uint rowNum;
22    uint fieldNum;
23 };
24
25 public class CSVFile : public File
26 {
27 public:
28    CSVFileParameters options;
29    CSVFileState info;
30
31    void PrintMessage(typed_object object, ...)
32    {
33       va_list args;
34       char buffer[4096];
35       va_start(args, object);
36       PrintStdArgsToBuffer(buffer, sizeof(buffer), object, args);
37       va_end(args);
38       OnMessage(buffer);
39    }
40
41    virtual bool OnMessage(String message)
42    {
43       ::PrintLn(this._class.name, ": ", message,
44             " lineNum=", info.lineNum,
45             " charNum=", info.charNum,
46             " rowNum=", info.rowNum,
47             " fieldNum=", info.fieldNum);
48    }
49
50    virtual bool OnRowStrings(Array<String> strings);
51
52    virtual void Process()
53    {
54       bool quoted = false, status = true;
55       Array<String> values { };
56       int start = 0, end = 0;
57       int readCount = 0;
58       Array<char> buffer { minAllocSize = 4096 };
59
60       //info.charNum = 0;
61       info.lineNum = 0;
62       info.rowNum = 0;
63       info.fieldNum = 0;
64
65       while(!Eof() && status)
66       {
67          int c, offset = 0;
68
69          if(start)
70          {
71             offset = readCount - start;
72             if(offset > buffer.minAllocSize / 2)
73                buffer.minAllocSize += 4096;
74             memmove(&buffer[0], &buffer[start], offset);
75             end -= start;
76             start = 0;
77          }
78
79          readCount = offset + Read(&buffer[offset], 1, buffer.minAllocSize - offset);
80          for(c = offset; c < readCount && status; c++)
81          {
82             char ch = buffer[c];
83             if(quoted)
84             {
85                if(ch == options.valueQuotes)
86                {
87                   quoted = false;
88                   end = c;
89                }
90             }
91             else
92             {
93                if(ch == options.valueQuotes)
94                {
95                   quoted = true;
96                   start = c + 1;
97                }
98                //else if(ch == options.fieldSeparator || ch == '\n')
99                else if(ch == options.fieldSeparator ||
100                      (ch == '\n' && (!options.tolerateNewLineInValues || info.fieldNum == options.expectedFieldCount-1)))
101                {
102                   int len = end-start;
103                   String value = new char[len+1];
104                   memcpy(value, &buffer[start], len);
105                   value[len] = 0;
106                   values.Add(value);
107                   start = end = 0;
108                   info.fieldNum++;
109                   if(ch == '\n')
110                   {
111                      info.lineNum++;
112                      info.rowNum++;
113                      status = OnRowStrings(values);
114                      values.Free();
115                      //info.charNum = 0;
116                      info.fieldNum = 0;
117                   }
118                }
119                else if(ch == '\r');
120                else
121                {
122                   if(!start)
123                      start = c;
124                   end = c;
125                }
126             }
127             //info.charNum++;
128          }
129       }
130       if(end > start)
131       {
132          int len = end-start;
133          String value = new char[len+1];
134          memcpy(value, &buffer[start], len);
135          value[len] = 0;
136          values.Add(value);
137       }
138       if(values.count && status)
139       {
140          status = OnRowStrings(values);
141          info.rowNum++;
142       }
143       values.Free();
144       delete values;
145    }
146 }