-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcsv17.cpp
More file actions
154 lines (131 loc) · 5.19 KB
/
Copy pathcsv17.cpp
File metadata and controls
154 lines (131 loc) · 5.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#https://github.com/ben-strasser/fast-cpp-csv-parser
#https://www.fluentcpp.com/2017/10/23/results-expressive-cpp17-coding-challenge/
/*
the filename of a CSV file,
the name of the column to overwrite in that file,
the string that will be used as a replacement for that column,
the filename where the output will be written.
For instance, if the CSV file had a column “City” with various values for the entries in the file, calling the tool with the name of the input file, City, London and the name of output file would result in a copy of the initial file, but with all cities set equal to “London”:
if the input file is empty, the program should write “input file missing” to the console.
if the input file does not contain the specified column, the program should write “column name doesn’t exists in the input file” to the console.
In both cases, there shouldn’t be any output file generated.
*/
/*
error in these lines:
for (auto& i: tokens) {
output << i;
output << (i == tokens.back() ? '\n':delimiter);
}
When some column has the same value as the last one in the row, it would print the endline after that column too as far as I understand.
Yeah, this seems obvious. I think the author just wanted to use a for-each loop and avoid an index but got a bit too clever.
I would just use a deque and pop the strings off as they're written, looks just as nice:
while (!tokens.empty()) {
output << tokens.pop_front() << (tokens.empty()? '\n' : delimiter);
}
The winner (and some other solutions) have a potentially exploitable bug: if a line contains too few columns, `token[target_index] = data` will assign to an out-of-range index.
Easily fixable via using `at()` instead of `[]`, but an interesting reminder that C++ still has sharp corners.
*/
/* Name: Fernando B. Giannasi
* Email: [email protected]
*
* Features of C++17 used:
* - std::filesystem
* - nodiscard and noreturn attributes
* - structured bindings
* - string_view
* - Template argument deduction for class templates
* - Init-statement for if/switch
* - std::optional
*/
#include <algorithm>
#include <experimental/filesystem>
#include <fstream>
#include <iostream>
#include <optional>
#include <string>
#include <string_view>
#include <sstream>
#include <utility>
#include <vector>
using namespace std;
namespace fs = std::experimental::filesystem;
[[nodiscard]] auto split_string(const string_view& input, const char delimiter)
{
stringstream ss {input.data()};
vector<string> result;
// result.reserve(count(begin(input), end(input), delimiter));
for (string buffer;
getline(ss, buffer, delimiter);)
{result.push_back(move(buffer));}
return result;
}
[[nodiscard]] optional<int> get_target_column(ifstream& input,
const string_view& label,
const char delimiter)
{
string first_line;
if (getline(input, first_line); // Init-statement for if/switch
first_line.size() == 0) { throw runtime_error("Input file missing"); }
input.seekg(0);
auto tokens = split_string(first_line, delimiter);
if (auto it = find(begin(tokens), end(tokens), label); // Init-statement for if/switch
it == tokens.end()) {
return {}; //return empty optional
}
else {
return distance(begin(tokens), it);
}
}
[[nodiscard]] auto get_file_handlers(const string_view& input, const string_view& output)
{
ifstream in_file {input.data(), ios::in};
if (!in_file.is_open()) { throw runtime_error("Unable to open input file"); }
ofstream out_file {output.data(), ios::out | ios::trunc};
if (!out_file.is_open()) { throw runtime_error("Unable to open output file");}
return pair(move(in_file), move(out_file)); //Template argument deduction for class templates (no make_pair)
}
void do_work(ifstream& input,
ofstream& output,
int target_index,
const string_view& new_value,
const char delimiter)
{
string buffer;
getline(input, buffer); // for the header line
output << buffer << endl;
while (getline(input, buffer)) {
auto tokens = split_string(buffer, delimiter);
tokens[target_index] = new_value.data();
for (auto& i: tokens) {
output << i;
output << (i == tokens.back() ? '\n':delimiter);
}
}
}
[[noreturn]] void usage_terminate(const string_view& progname) noexcept
{
cout << "Usage: " << progname << " [IN_FILE] [COLUMN] [NEW_VALUE] [OUT_FILE]" << endl;
abort();
}
int main(int argc, char* argv[])
{
try
{
if (argc != 5) { throw runtime_error("Bad arguments"); }
auto [in_file, out_file] = get_file_handlers(argv[1], argv[4]);
string_view new_value = argv[3];
auto target_index = get_target_column(in_file, argv[2], ',');
if (target_index) {
do_work(in_file, out_file, *target_index, new_value, ',');
}
else {
throw runtime_error("Column name doesn’t exist in the input file");
}
}
catch (runtime_error& e)
{
cout << e.what() << endl;
usage_terminate(argv[0]);
}
return 0;
}