Merge pull request #613 from cduret/csv_lib

add csv library
This commit is contained in:
Mark Thom
2020-06-27 16:28:10 -03:00
committed by GitHub
2 changed files with 143 additions and 0 deletions

View File

@@ -428,6 +428,9 @@ The modules that ship with Scryer Prolog are also called
`load_html/3` and `load_xml/3` represent HTML and XML documents
as Prolog terms for convenient and efficient reasoning. Use
`library(xpath)` to extract information from parsed documents.
* [`csv`](src/lib/csv.pl)
`parse_csv//1` and `parse_csv//2` can be used with [`phrase_from_file/2`](src/lib/pio.pl)
or [`phrase/2`](src/lib/dcgs.pl) to parse csv
* [`xpath`](src/lib/xpath.pl)
The predicate `xpath/3` is used for convenient reasoning about
HTML and XML documents, inspired by the XPath language. This

140
src/lib/csv.pl Normal file
View File

@@ -0,0 +1,140 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Predicates for parsing CSV data
Only two options are provided with default values :
- token_separator(',')
- skip_header(false)
Examples
* parsing a csv string:
?- use_module(library(csv)).
?- use_module(library(dcgs)).
?- phrase(parse_csv(Data), "col1,col2,col3,col4\none,2,,three").
Data = frame(["col1","col2","col3","col4"],[["one",2,[],"three"]]).
* with some options:
?- phrase(parse_csv(Data, [skip_header(true),token_separator(';')]), "col1;col2;col3,col4\none;2;;three").
Data = frame([],[["one",2,[],"three"]]).
* parsing a csv file:
?- use_module(library(csv)).
?- use_module(library(pio)).
?- phrase_from_file(parse_csv(frame(Header, Rows)), './test.csv').
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
:- module(csv, [
parse_csv//1,
parse_csv//2
]).
:- use_module(library(dcgs)).
:- use_module(library(lists)).
option(W, O) :-
( member(W, O) -> true
; throw(not_found_error(W, O))).
option_extends([], Opt, Opt).
option_extends([X | Y], Opt0, Opt) :-
functor(X, Name, 1),
F0 =.. [Name, _],
( select(F0, Opt0, R) ->
option_extends(Y, [X | R], Opt)
; option_extends(Y, [X | Opt0], Opt) ).
tokens([], Opt), [Tk_Sep] -->
{ option(token_separator(Tk_Sep), Opt) },
[Tk_Sep],
!.
tokens([], _), "\r\n" -->
"\r\n",
!.
tokens([], _), "\n" -->
"\n",
!.
tokens([], _), "\r" -->
"\r",
!.
tokens([X | Y], Opt) -->
[X],
!,
tokens(Y, Opt).
tokens([], _) --> [].
field(R, Opt) -->
"\"",
!,
string_tokens(R, Opt).
field(R, Opt) -->
tokens(R0, Opt),
{ R0 \== [],
catch(number_chars(R, R0), _, R = R0)
}.
field([], _) --> [].
string_tokens(R, Opt) -->
[X],
( { X == '"' } ->
( "\"" ->
{ R = [X | Y] },
string_tokens(Y, Opt)
; { R = [] })
; { R = [X | Y] },
string_tokens(Y, Opt)).
end_token --> "\r\n".
end_token --> "\n".
end_token --> "\r".
end_token --> [].
separator(Opt) -->
{ option(token_separator(Tk_Sep), Opt) },
[Tk_Sep].
row([X | Y], Opt) -->
field(X, Opt),
!,
( separator(Opt) ->
row(Y, Opt)
; end_token ->
{ Y = [] }).
rows(R, Opt) -->
row(X, Opt),
!,
( { X \== [[]] } ->
rows(Y, Opt),
{ R = [X | Y] }
; { R = [] }).
parse_csv(frame(Header, Rows), Opt) -->
{ option_extends(Opt, [
skip_header(false),
token_separator(',')
], Opt0)
},
( { member(skip_header(false), Opt0) } ->
row(Header, Opt0),
{ Header \== [[]] },
end_token
; row(_, Opt0),
end_token,
{ Header = [] }),
rows(Rows, Opt0).
parse_csv(R) -->
parse_csv(R, []).