可逆CSV解析

kmynzznz  于 2023-07-31  发布在  其他
关注(0)|答案(4)|浏览(131)

Prolog新手在这里。在SWI Prolog中,我试图弄清楚如何可逆地解析一行简单的CSV,但我被卡住了。这是我得到的

csvstring1(S, L) :-
  split_string(S, ',', ',', T),
  maplist(atom_number, T, L).
   
csvstring2(S, L) :-
  atomic_list_concat(T, ',', S),
  maplist(atom_number, T, L).

% This one is the same except that maplist comes first. 
csvstring3(S, L) :-
  maplist(atom_number, T, L),
  atomic_list_concat(T, ',', S).

字符串
现在csvstring1和csvstring2以“向前”的方式工作:

?- csvstring1('1,2,3,4', L).
L = [1, 2, 3, 4].

?- csvstring2('1,2,3,4', L).
L = [1, 2, 3, 4].


但不是csvstring3:

?- csvstring3('1,2,3,4', L).
ERROR: Arguments are not sufficiently instantiated


此外,csvstring3反向工作,而不是其他两个 predicate :

?- csvstring3(L, [1,2,3,4]).
L = '1,2,3,4'.

?- csvstring1(L, [1,2,3,4]).
ERROR: Arguments are not sufficiently instantiated

?- csvstring2(L, [1,2,3,4]).
ERROR: Arguments are not sufficiently instantiated


如何将这些组合成一个 predicate ?

0g0grzrc

0g0grzrc1#

其他人给出了一些建议和很多代码。使用SWI-Prolog,要解析逗号分隔的整数,您可以使用library(dcg/basics)和library(dcg/high_order)来简单地执行此操作:

?- use_module(library(dcg/basics)),
   use_module(library(dcg/high_order)),
   portray_text(true).
true.

?- phrase(sequence(integer, ",", Ns), `1,2,3,4`).
Ns = [1, 2, 3, 4].

?- phrase(sequence(integer, ",", [-7,6,42]), S).
S = `-7,6,42`.

字符串
当然,如果你试图解析真实的的CSV文件,你应该使用CSV解析器。下面是一个阅读CSV文件并将其输出写入TSV(制表符分隔)文件的最小示例。如果这是您在名为example.csv的文件中的输入:

$ cat example.csv
id,name,salary,department
1,john,2000,sales
2,Andrew,5000,finance
3,Mark,8000,hr
4,Rey,5000,marketing
5,Tan,4000,IT


您可以从文件中读取它,并使用制表符作为分隔符,如下所示:

?- csv_read_file('example.csv', Data),
   csv_write_file('example.tsv', Data).
Data = [row(id, name, salary, department),
        row(1, john, 2000, sales),
        row(2, 'Andrew', 5000, finance),
        row(3, 'Mark', 8000, hr),
        row(4, 'Rey', 5000, marketing),
        row(5, 'Tan', 4000, 'IT')].


库根据文件扩展名猜测字段分隔符。在这里,它正确地猜到了'csv'是指逗号“,'tsv'是指制表符。我们可以使用cat -t显式显示选项卡。

$ cat example.tsv 
id  name    salary  department
1   john    2000    sales
2   Andrew  5000    finance
3   Mark    8000    hr
4   Rey 5000    marketing
5   Tan 4000    IT
$ cat -t example.tsv 
id^Iname^Isalary^Idepartment^M
1^Ijohn^I2000^Isales^M
2^IAndrew^I5000^Ifinance^M
3^IMark^I8000^Ihr^M
4^IRey^I5000^Imarketing^M
5^ITan^I4000^IIT^M

3df52oht

3df52oht2#

我不知道一个特别的新手友好的方式来做到这一点,不妥协的地方。这是最简单的:

csvString_list(String, List) :-
    ground(String),
    atomic_list_concat(Temp, ',', String),
    maplist(atom_number, Temp, List).

csvString_list(String, List) :-
    ground(List),
    maplist(atom_number, Temp, List),
    atomic_list_concat(Temp, ',', String).

字符串
但它会产生并留下虚假的选择点,这有点烦人。
这削减了choicepoints,这在使用它时很好,但在不知道这意味着什么的情况下进行的练习很糟糕:

csvString_list(String, List) :-
    ground(String),
    atomic_list_concat(Temp, ',', String),
    maplist(atom_number, Temp, List),
    !.

csvString_list(String, List) :-
    ground(List),
    maplist(atom_number, Temp, List),
    atomic_list_concat(Temp, ',', String).


这使用了if/else,这是更少的代码:

csvString_list(String, List) :-
  ground(String) ->
      (atomic_list_concat(Temp, ',', String), maplist(atom_number, Temp, List))
    ; (maplist(atom_number, Temp, List),      atomic_list_concat(Temp, ',', String)).


但是是logically bad and you should reify the branching with if_,它不是内置在SWI Prolog中的,使用起来不那么简单。
或者你可以用DCG写一个语法,这不是新手的领域:

:- set_prolog_flag(double_quotes, chars).
:- use_module(library(dcg/basics)).

csvTail([N|Ns]) --> [','], number(N), csvTail(Ns). 
csvTail([])     --> [].

csv([N|Ns]) --> number(N), csvTail(Ns).


例如。

?- phrase(csv(Ns), "11,22,33,44,55").
Ns = [11, 22, 33, 44, 55]

?- phrase(csv([11, 22, 33, 44, 55]), String)
String = [49, 49, ',', 50, 50, ',', 51, 51, ',', 52, 52, ',', 53, 53]


但是现在你又回到了它,在解析 * 和 * 时留下了虚假的选择点,你必须处理SWI Prolog中字符串/原子/字符代码的历史性分裂;由于double_quotes标志,该列表将与"11,22,33,44,55"统一,但看起来不会。

62lalag4

62lalag43#

split_string不可逆。可以使用DCG -这里是一个简单的CSV多行DCG解析器:

% Nicer formatting
% https://www.swi-prolog.org/pldoc/man?section=flags
:- set_prolog_flag(answer_write_options, [quoted(true), portray(true), spacing(next_argument), max_depth(100), attributes(portray)]).

% Show lists of codes as text (if 3 chars or longer)
:- portray_text(true).

csv_lines([]) --> [].
% Newline after every line
csv_lines([H|T]) --> csv_fields(H), [10], csv_lines(T).

csv_fields([H|T]) --> csv_field(H), csv_field_end(T).

csv_field_end([]) --> [].
% Comma between fields
csv_field_end(T) --> [44], csv_fields(T).

csv_field([]) --> [].
csv_field([H|T]) -->
    [H],
    % Fields cannot contain comma, newline or carriage return
    { maplist(dif(H), [44, 10, 13]) },
    csv_field(T).

字符串
为了证明可逆性:

% Note: z is char 122
?- phrase(csv_lines([[`def`, `cool`], [`abc`, [122]]]), Lines).
Lines = `def,cool\nabc,z\n` ;
false.

?- phrase(csv_lines(Fields), `def,cool\nabc,z\n`).
Fields = [[`def`, `cool`], [`abc`, [122]]] ;
false.


要解析字段内容并保持可逆性,可以使用例如atom_codes

lo8azlld

lo8azlld4#

如何将这些组合成一个 predicate ?

csvstring(S, L) :-
  (  ground(S)
  -> atomic_list_concat(T, ',', S),
     maplist(atom_number, T, L)
  ;  maplist(atom_number, T, L),
     atomic_list_concat(T, ',', S)
  ).

字符串
。微测试。。

?- csvstring('1,2,3,4', L).
L = [1, 2, 3, 4].

?- csvstring(L, [1,2,3,4]).
L = '1,2,3,4'.

相关问题