#!/usr/bin/c++script #include /* Split a csv line. I don't know how to do this using ranges... This function is really meant to be used by the FILENAME::tocsv() function. The function avoids allocating/copying stuff. It fills the columns vector. It simply maps when possible the string_views in the columns vector directly in the input line. Since it is used in a RANGE expression there is no problem with string_view dangling. The function uses the comma as a separator. It also supports double quote to allow spaces and commas. A quote immediatly followed by a quote is taken as a quote inside the value. The resulting string_view does not contain the surronding quotes and the internal quotes are processed. So "this is a "" quote" becomes this is a " quote Normally the string_view is simply pointing to a part of the imput line. Nothing is copied. There is a copy in the buffer variable only when there is quote inside a value (like above). The string_view point to a part of the buffer . The columns vector is pre initialized with the number of columns we want to extract. So if the CSV line contains more fields, they won't appear. If the line contains less fields, the remaining string_views in the vector will be initialized to "". */ void split_csv_line ( std::string_view line, vector &columns, std::string &buffer) { buffer.clear(); unsigned nbfilled = 0; const char *start = line.begin(); while (start != line.end() && nbfilled < columns.size()){ while (isspace(*start) && start != line.end()) start++; if (start == line.end()) break; const char *pt = start; if (*pt == '"'){ pt++; start = pt; auto buffer_start = buffer.begin() + buffer.size(); bool copy_started = false; // By default, we avoid copying in buffer bool end_seen = false; while (pt != line.end()){ if (*pt == '"'){ if ((pt+1) != line.end() && pt[1] == '"'){ pt++; if (!copy_started){ copy_started=true; buffer += string(start,pt-start); } pt++; }else{ // End quote seen, we skip to the end if (copy_started){ columns[nbfilled++] = string_view(buffer_start,buffer.end()); }else{ columns[nbfilled++] = string_view(start,pt); } pt++; while (pt != line.end() && *pt != ',') pt++; end_seen = true; break; } }else{ if (copy_started) buffer += *pt; pt++; } } if (!end_seen){ // Incomplete quoted value, we copy what was there anyway columns[nbfilled++] = string_view(start,pt); } }else{ while (pt != line.end() && *pt != ',') pt++; if (pt > start) columns[nbfilled++] = string_view(start,pt); } if (pt != line.end() && *pt == ',') pt++; start = pt; } while (nbfilled < columns.size()) columns[nbfilled++] = ""; } auto tocsv(unsigned nbcolumns) { vector columns(nbcolumns); return views::transform([columns,buffer=string()] (auto &line) mutable{ using type = std::remove_reference_t; if constexpr (std::is_same_v){ split_csv_line(line,columns,buffer); }else if constexpr (std::is_same_v){ split_csv_line(line,columns,buffer); }else if constexpr (std::is_same_v){ split_csv_line(line.line,columns,buffer); }else{ static_assert (false,"cpps::csv: type not supported"); } return columns; }); } #main option filename ('f',"file","Nom du fichier","/tmp/toto.txt",false); option dups ('d',"dups","copie N fois le contenu",1,false); FILENAME file(filename.val); vector content={ "allo,comment,ca,va", "--", "allo,comment,ca", "allo,comment,ca,va,le,monde", "allo,\"comment\",ca", "allo ,\"comment\" ,ca", "allo,\"com\"\"ment\",ca", "allo,\"com\"\"ment", }; cout << format ("Produit le fichier {}\n",filename.val); FILE *fout = fopen (filename.val.c_str(),"w"); for (unsigned i=0; i