From 1c5536ea9cabddb38af1389f75b743f7a86d15e2 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 16 Oct 2013 10:31:31 +0200 Subject: [PATCH] ltlcross: follow RFC 4180 for CSV output. * src/misc/escape.cc, src/misc/escape.hh (escape_rfc4180): New function. * src/bin/ltlcross.cc: Do not output space after ',', use "\r\n" for end of line, and use escape_rfc4180(). * NEWS: Mention it. --- NEWS | 3 +- src/bin/ltlcross.cc | 95 +++++++++++++++++++++++---------------------- src/misc/escape.cc | 16 ++++++++ src/misc/escape.hh | 20 +++++++--- 4 files changed, 81 insertions(+), 53 deletions(-) diff --git a/NEWS b/NEWS index 51d180819..519322801 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,7 @@ New in spot 1.2a (not released) - Nothing yet. + * Bug fixes: + - ltlcross' CSV output now stricly follows RFC 4180. New in spot 1.2 (2013-10-01) diff --git a/src/bin/ltlcross.cc b/src/bin/ltlcross.cc index 0a6b66f1d..69c263b48 100644 --- a/src/bin/ltlcross.cc +++ b/src/bin/ltlcross.cc @@ -275,46 +275,46 @@ struct statistics static void fields(std::ostream& os) { - os << (" \"states\"," - " \"edges\"," - " \"transitions\"," - " \"acc\"," - " \"scc\"," - " \"nonacc_scc\"," - " \"terminal_scc\"," - " \"weak_scc\"," - " \"strong_scc\"," - " \"nondet_states\"," - " \"nondet_aut\"," - " \"terminal_aut\"," - " \"weak_aut\"," - " \"strong_aut\"," - " \"time\"," - " \"product_states\"," - " \"product_transitions\"," - " \"product_scc\""); + os << ("\"states\"," + "\"edges\"," + "\"transitions\"," + "\"acc\"," + "\"scc\"," + "\"nonacc_scc\"," + "\"terminal_scc\"," + "\"weak_scc\"," + "\"strong_scc\"," + "\"nondet_states\"," + "\"nondet_aut\"," + "\"terminal_aut\"," + "\"weak_aut\"," + "\"strong_aut\"," + "\"time\"," + "\"product_states\"," + "\"product_transitions\"," + "\"product_scc\""); } void to_csv(std::ostream& os) { - os << states << ", " - << edges << ", " - << transitions << ", " - << acc << ", " - << scc << ", " - << nonacc_scc << ", " - << terminal_scc << ", " - << weak_scc << ", " - << strong_scc << ", " - << nondetstates << ", " - << nondeterministic << ", " - << terminal_aut << ", " - << weak_aut << ", " - << strong_aut << ", " - << time << ", " - << product_states << ", " - << product_transitions << ", " + os << states << ',' + << edges << ',' + << transitions << ',' + << acc << ',' + << scc << ',' + << nonacc_scc << ',' + << terminal_scc << ',' + << weak_scc << ',' + << strong_scc << ',' + << nondetstates << ',' + << nondeterministic << ',' + << terminal_aut << ',' + << weak_aut << ',' + << strong_aut << ',' + << time << ',' + << product_states << ',' + << product_transitions << ',' << product_scc; } }; @@ -935,7 +935,7 @@ namespace if (first) first = false; else - err << ","; + err << ','; err << l << i; } err << "} disagree with {"; @@ -946,7 +946,7 @@ namespace if (first) first = false; else - err << ","; + err << ','; err << l << i; } err << "} when evaluating "; @@ -1322,6 +1322,7 @@ namespace }; } +// Output an RFC4180-compatible CSV file. static void print_stats_csv(const char* filename) { @@ -1342,20 +1343,20 @@ print_stats_csv(const char* filename) unsigned rounds = vstats.size(); assert(rounds == formulas.size()); - *out << "\"formula\", \"tool\", "; + *out << "\"formula\",\"tool\","; statistics::fields(*out); - *out << "\n"; + *out << "\r\n"; for (unsigned r = 0; r < rounds; ++r) for (unsigned t = 0; t < ntrans; ++t) if (vstats[r][t].ok) { *out << "\""; - spot::escape_str(*out, formulas[r]); - *out << "\", \""; - spot::escape_str(*out, translators[t]); - *out << "\", "; + spot::escape_rfc4180(*out, formulas[r]); + *out << "\",\""; + spot::escape_rfc4180(*out, translators[t]); + *out << "\","; vstats[r][t].to_csv(*out); - *out << "\n"; + *out << "\r\n"; } delete outfile; } @@ -1394,7 +1395,7 @@ print_stats_json(const char* filename) *out << "\",\n \""; spot::escape_str(*out, formulas[r]); } - *out << ("\"\n ],\n \"fields\": [\n \"formula\", \"tool\","); + *out << ("\"\n ],\n \"fields\": [\n \"formula\",\"tool\","); statistics::fields(*out); *out << "\n ],\n \"inputs\": [ 0, 1 ],"; *out << "\n \"results\": ["; @@ -1404,9 +1405,9 @@ print_stats_json(const char* filename) if (vstats[r][t].ok) { if (notfirst) - *out << ","; + *out << ','; notfirst = true; - *out << "\n [ " << r << ", " << t << ", "; + *out << "\n [ " << r << ',' << t << ','; vstats[r][t].to_csv(*out); *out << " ]"; } diff --git a/src/misc/escape.cc b/src/misc/escape.cc index 1e90e298b..921addcfc 100644 --- a/src/misc/escape.cc +++ b/src/misc/escape.cc @@ -31,6 +31,22 @@ namespace spot { + std::ostream& + escape_rfc4180(std::ostream& os, const std::string& str) + { + for (std::string::const_iterator i = str.begin(); i != str.end(); ++i) + switch (*i) + { + case '"': + os << "\"\""; + break; + default: + os << *i; + break; + } + return os; + } + std::ostream& escape_str(std::ostream& os, const std::string& str) { diff --git a/src/misc/escape.hh b/src/misc/escape.hh index c603d9e52..37f086f29 100644 --- a/src/misc/escape.hh +++ b/src/misc/escape.hh @@ -32,16 +32,26 @@ namespace spot /// \addtogroup misc_tools /// @{ - /// \brief Escape characters ", \\, and - /// \\n in \a str. - SPOT_API std::ostream& escape_str(std::ostream& os, const std::string& str); + /// \brief Double characters " in strings. + /// + /// In CSV files, as defined by RFC4180, double-quoted string that + /// contain double-quotes should simply duplicate those quotes. + SPOT_API std::ostream& + escape_rfc4180(std::ostream& os, const std::string& str); /// \brief Escape characters ", \\, and /// \\n in \a str. - SPOT_API std::string escape_str(const std::string& str); + SPOT_API std::ostream& + escape_str(std::ostream& os, const std::string& str); + + /// \brief Escape characters ", \\, and + /// \\n in \a str. + SPOT_API std::string + escape_str(const std::string& str); /// \brief Remove spaces at the front and back of \a str. - SPOT_API void trim(std::string& str); + SPOT_API void + trim(std::string& str); /// @} }