Here is a code which accepts inside the fields delimited by double-quotes:
- commas
- carriage-returns
- any character different from double-quotes
This solution is not fully compliant with the standard, particularly with the point 6 of page 3, where it is written that a double-quotes may exist in a field.
This solution has not been tried on large files.
Preliminary
Generation of the file to import :
Export["file.txt", "a1,b1,c1,d1,\"e1
e1
e1\"
a2,b2,\"c2
c2
c2\",d2,e2", "Text"]
Importation of the whole file as a single string:
text00 = Import["file.txt", "Text"]
Here is a utility for nice rendering of the CarriageReturn/Line-Feed (CRLF) :
toVisibleCRLF[exp_] := exp /. str_String :>
Row[StringSplit[str , "\n" -> Style["CRLF", Red]] ];
Test of the utility on the imported text :
toVisibleCRLF[text00]
Parsing
The idea is to isolate the shortest pairs of quotes (1) by wrapping them in a inert structure called doNotSplit[]
:
list00 = StringSplit[text00, str : Shortest["\"" ~~ ___ ~~ "\""] :> doNotSplit[str]];
list00 // toVisibleCRLF // Column
Then the proper substrings are splitted at the commas and carriage-return.
Commas are eliminated.
Carriage-returns are maintained because we will need them for further record separations:
list01 = Flatten[
If[Head[#] =!= doNotSplit,
StringSplit[#, {",", "\n" -> "\n"}], #] & /@ list00];
list01 // toVisibleCRLF // Column
Next records are isolated:
list02 = Split[list01, And[#1 =!= "\n", #2 =!= "\n"] &];
list02 // toVisibleCRLF // (Grid[{#}] & /@ # &) // Column
The code list02 = Split[list01, And[#1 =!= "\n", #2 =!= "\n"] &]
is equivalent to
list02 = Split[list01, ! Or[#1 === "\n", #2 === "\n"] &]
. It has splitted the list before and after each \n
. That's the simplest way the remove the \n
afterwards.
The final result is obtained after removing of the \n
and the doNotSplit
:
result = DeleteCases[list02 /. doNotSplit[str_] :> str, {"\n"}];
result // Grid
The whole useful code :
text00 = Import["file.txt", "Text"];
list00 = StringSplit[text00,
str : Shortest["\"" ~~ ___ ~~ "\""] :> doNotSplit[str]];
list01 = Flatten[
If[Head[#] =!= doNotSplit,
StringSplit[#, {",", "\n" -> "\n"}], #] & /@ list00];
list02 = Split[list01, And[#1 =!= "\n", #2 =!= "\n"] &];
result = DeleteCases[list02 /. doNotSplit[str_] :> str, {"\n"}]
Edit
(1) "shortest" in the meaning of the Shortest[]
of Mathematica, which is a little bit special. For example, the pattern Shortest["A"~~__~~"B"]
assumes the whole text is read from left to right and it takes the text between the first "A" and the next "B".
Of course, if there are several sequences "A...B" Mathematica redo it again.