Skip to content

Commit

Permalink
Only quote strings on output if needed.
Browse files Browse the repository at this point in the history
Closes #116
  • Loading branch information
hadley committed Apr 16, 2015
1 parent 9a6cb62 commit a95d623
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 4 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# readr 0.1.0.9000

* In `write_csv()`, only use quotes when they're actually needed (#116)
6 changes: 5 additions & 1 deletion R/write.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
#'
#' This is about twice as fast as \code{\link{write.csv}}, and never
#' writes row names. Non-atomic vectors are coerced to character vectors
#' with \code{as.character}. All columns are encoded as UTF-8.
#' with \code{as.character}. All columns are encoded as UTF-8. Values are
#' only quoted if needed: if they contain a comma, quote or new line.
#'
#' @param x A data frame to write to disk
#' @param path Path to write to. If \code{""} will return the csv file as
Expand All @@ -14,6 +15,9 @@
#' @examples
#' cat(write_csv(head(mtcars), ""))
#' cat(write_csv(head(iris), ""))
#'
#' df <- data.frame(x = c("a", '"', ",", "\n"))
#' read_csv(write_csv(df, ""))
write_csv <- function(x, path, append = FALSE, col_names = !append) {
stopifnot(is.data.frame(x))

Expand Down
6 changes: 5 additions & 1 deletion man/write_csv.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@ will append to an existing file.}
\description{
This is about twice as fast as \code{\link{write.csv}}, and never
writes row names. Non-atomic vectors are coerced to character vectors
with \code{as.character}. All columns are encoded as UTF-8.
with \code{as.character}. All columns are encoded as UTF-8. Values are
only quoted if needed: if they contain a comma, quote or new line.
}
\examples{
cat(write_csv(head(mtcars), ""))
cat(write_csv(head(iris), ""))

df <- data.frame(x = c("a", '"', ",", "\\n"))
read_csv(write_csv(df, ""))
}

17 changes: 15 additions & 2 deletions src/write_csv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,21 @@ void stream_csv_row(Stream& output, Rcpp::List x, int i) {
output << '\n';
}

bool needs_quote(const char* string, char delim = ',') {
for (const char* cur = string; *cur != '\0'; ++cur) {
if (*cur == '\n' || *cur == '\r' || *cur == '"' || *cur == delim)
return true;
}

return false;
}

template <class Stream>
void stream_csv(Stream& output, const char* string) {
output << '"';
bool quotes = needs_quote(string);

if (quotes)
output << '"';

for (const char* cur = string; *cur != '\0'; ++cur) {
switch (*cur) {
Expand All @@ -32,7 +44,8 @@ void stream_csv(Stream& output, const char* string) {
}
}

output << '"';
if (quotes)
output << '"';
}

template <class Stream>
Expand Down
17 changes: 17 additions & 0 deletions tests/testthat/test-write.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
context("write_csv")

test_that("strings are only quoted if needed", {
x <- c("a", ',')

csv <- write_csv(data.frame(x), "", col_names = FALSE)
expect_equal(csv, 'a\n","\n')
})

test_that("read_csv and write_csv round trip special chars", {
x <- c("a", '"', ",", "\n")

output <- data.frame(x)
input <- read_csv(write_csv(output, ""))

expect_equal(input$x, x)
})

0 comments on commit a95d623

Please sign in to comment.