---------------------------------------------------------------------- JoinCSVLine() This function is the inverse of SplitCSVLineWithDelimiter() in that the string returned by JoinCSVLineWithDelimiter() can be passed to SplitCSVLineWithDelimiter() to get the original string vector back. Quotes and escapes the elements of original_cols according to CSV quoting rules, and the joins the escaped quote
| 163 | // ---> [Google,x,"Buchheit, Paul","string with "" quote in it"," space "] |
| 164 | // ---------------------------------------------------------------------- |
| 165 | void JoinCSVLineWithDelimiter(const vector<string>& cols, char delimiter, |
| 166 | string* output) { |
| 167 | CHECK(output); |
| 168 | CHECK(output->empty()); |
| 169 | vector<string> quoted_cols; |
| 170 | |
| 171 | const string delimiter_str(1, delimiter); |
| 172 | const string escape_chars = delimiter_str + "\""; |
| 173 | |
| 174 | // If the string contains the delimiter or " anywhere, or begins or ends with |
| 175 | // whitespace (ie ascii_isspace() returns true), escape all double-quotes and |
| 176 | // bracket the string in double quotes. string.rbegin() evaluates to the last |
| 177 | // character of the string. |
| 178 | for (const auto& col : cols) { |
| 179 | if ((col.find_first_of(escape_chars) != string::npos) || |
| 180 | (!col.empty() && (ascii_isspace(*col.begin()) || |
| 181 | ascii_isspace(*col.rbegin())))) { |
| 182 | // Double the original size, for escaping, plus two bytes for |
| 183 | // the bracketing double-quotes, and one byte for the closing \0. |
| 184 | int size = 2 * col.size() + 3; |
| 185 | unique_ptr<char[]> buf(new char[size]); |
| 186 | |
| 187 | // Leave space at beginning and end for bracketing double-quotes. |
| 188 | int escaped_size = strings::EscapeStrForCSV(col.c_str(), |
| 189 | buf.get() + 1, size - 2); |
| 190 | CHECK_GE(escaped_size, 0) << "Buffer somehow wasn't large enough."; |
| 191 | CHECK_GE(size, escaped_size + 3) |
| 192 | << "Buffer should have one space at the beginning for a " |
| 193 | << "double-quote, one at the end for a double-quote, and " |
| 194 | << "one at the end for a closing '\0'"; |
| 195 | *buf.get() = '"'; |
| 196 | *((buf.get() + 1) + escaped_size) = '"'; |
| 197 | *((buf.get() + 1) + escaped_size + 1) = '\0'; |
| 198 | quoted_cols.push_back(string(buf.get(), buf.get() + escaped_size + 2)); |
| 199 | } else { |
| 200 | quoted_cols.push_back(col); |
| 201 | } |
| 202 | } |
| 203 | JoinStrings(quoted_cols, delimiter_str, output); |
| 204 | } |
| 205 | |
| 206 | void JoinCSVLine(const vector<string>& cols, string* output) { |
| 207 | JoinCSVLineWithDelimiter(cols, ',', output); |
no test coverage detected