Take a look at the following modification that I made which embodies several changes from the default csv.c extension module. <http://www.dessus.com/files/vsv.c> ``` /* ** 2020-02-08 modified by Keith Medcalf who also disclaims all copyright ** on the modifications and hereby places this code in the public domain ** ** This file contains the implementation of an SQLite virtual table for ** reading VSV (Variably Separated Values), which are like CSV files, ** but subtly different. VSV supports a number of extensions to the ** CSV format as well as more processing options. ** ** ** Usage: ** ** create virtual table temp.vsv using vsv(...); ** select * from vsv; ** ** The parameters to the vsv module (the vsv(...) part) are as follows: ** ** filename=STRING the filename, passed to the Operating System ** data=STRING alternative data ** columns=N columns parsed from the VSV file ** header=BOOL whether or not a header row is present ** skip=N number of leading data rows to skip ** rsep=STRING record separator ** fsep=STRING field separator ** validatetext=BOOL validate UTF-8 encoding of text fields ** affinity=AFFINITY affinity to apply to each returned value ** nulls=BOOL empty fields are returned as NULL ** ** ** Defaults: ** ** filename / data nothing. You must provide one or the other ** it is an error to provide both or neither ** schema nothing. If not provided then one will be ** generated for you from the header, or if no ** header is available then autogenerated using ** field names manufactured as cX where X is the ** column number ** columns nothing. If not specified then the number of ** columns is determined by counting the fields ** in the first record of the VSV file (which ** will be the header row if header is specified), ** the number of columns is not parsed from the ** schema even if one is provided ** header=no no header row in the VSV file ** skip=0 do not skip any data rows in the VSV file ** fsep=',' default field separator is a comma ** rsep='\n' default record separator is a newline ** validatetext=no do not validate text field encoding ** affinity=none do not apply affinity to each returned value ** nulls=off empty fields returned as zero-length ** ** ** Parameter types: ** ** STRING means a quoted string ** N means a whole number not containing a sign ** BOOL means something that evaluates as true or false ** it is case insensitive ** yes, no, true, false, 1, 0 ** AFFINITY means an SQLite3 type specification ** it is case insensitive ** none, blob, text, integer, real, numeric ** ** STRING means a quoted string. The quote character may be either ** a single quote or a double quote. Two quote characters in a row ** will be replaced with a single quote character. STRINGS do not ** need to be quoted if it is obvious where they begin and end ** (that is, they do not contain a comma). Leading and trailing ** spaces will be trimmed from unquoted strings. ** ** filename =./this/filename.here, ... ** filename =./this/filename.here , ... ** filename = ./this/filename.here, ... ** filename = ./this/filename.here , ... ** filename = './this/filename.here', ... ** filename = "./this/filename.here", ... ** ** are all equivalent. ** ** BOOL defaults to true so the following specifications are all the ** same: ** ** header = true ** header = yes ** header = 1 ** header ** ** ** Specific Parameters: ** ** The platform/compiler/OS fopen call is responsible for interpreting ** the filename. It may contain anything recognized by the OS. ** ** The separator string containing exactly one character, or a valid ** escape sequence. Recognized escape sequences are: ** ** \t horizontal tab, ascii character 9 (0x09) ** \n linefeed, ascii character 10 (0x0a) ** \v vertical tab, ascii character 11 (0x0b) ** \f form feed, ascii character 12 (0x0c) ** \xhh specific byte where hh is hexadecimal ** ** The validatetext setting will cause the validity of the field ** encoding (not its contents) to be verified. It effects how ** fields that are supposed to contain text will be returned to ** the SQLite3 library in order to prevent invalid utf8 data from ** being stored or processed as if it were valid utf8 text. ** ** The nulls option will cause fields that do not contain anything ** to return NULL rather than an empty result. Two separators ** side-by-each with no intervening characters at all will be ** returned as NULL if nulls is true and if nulls is false or ** the contents are explicity empty ("") then a 0 length blob ** (if affinity=blob) or 0 length text string. ** ** For the affinity setting, the following processing is applied to ** each value returned by the VSV virtual table: ** ** none no affinity is applied, all fields will be ** returned as text just like in the original ** csv module, embedded nulls will terminate ** the text. if validatetext is in effect then ** an error will be thrown if the field does ** not contain validly encoded text or contains ** embedded nulls ** ** blob all fields will be returned as blobs ** validatetext has no effect ** ** text all fields will be returned as text just ** like in the original csv module, embedded ** nulls will terminate the text. ** if validatetext is in effect then a blob ** will be returned if the field does not ** contain validly encoded text or the field ** contains embedded nulls ** ** integer if the field data looks like an integer, ** (regex "^ *(\+|-)?\d+ *$"), ** then an integer will be returned as ** provided by the compiler and platform ** runtime strtoll function ** otherwise the field will be processed as ** text as defined above ** ** real if the field data looks like a number, ** (regex "^ *(\+|-)?(\d+\.?\d*|\d*\.?\d+)([eE](\+|-)?\d+)? *$") ** then a double will be returned as ** provided by the compiler and platform ** runtime strtold function otherwise the ** field will be processed as text as ** defined above ** ** numeric if the field looks like an integer ** (see integer above) that integer will be ** returned ** if the field looks like a number ** (see real above) then the number will ** returned as an integer if it has no ** fractional part and ** (a) your platform/compiler supports ** long double and the number will fit in ** a 64-bit integer; or, ** (b) your platform/compiler does not ** support long double (treats it as a double) ** then a 64-bit integer will only be returned ** if the value would fit in a 6-byte varint, ** otherwise a double will be returned ** ** The nulls option will cause fields that do not contain anything ** to return NULL rather than an empty result. Two separators ** side-by-each with no intervening characters at all will be ** returned as NULL if nulls is true and if nulls is false or ** the contents are explicity empty ("") then a 0 length blob ** (if affinity=blob) or 0 length text string will be returned. ** */ ``` A set of various extensions including vsv.c can be found in <http://www.dessus.com/files/sqlite3extensions.zip>