Handling Semicolons in CSV to Prevent Splitting Across Cells in Go

I am generating a CSV file in a Go HTTP handler and sending it as a response. Some of my text fields contain semicolons (;), which causes issues when opening the file in applications like Excel. The data is getting split into multiple cells instead of appearing in a single cell.

For example, in my case:

  • The name field contains google;gmail
  • The URL field contains https://www.goo;gle.com/

Here’s my current Go code:

func routeReferencesCrossrefDownloadPost(w http.ResponseWriter, r *http.Request) {
	var req DownloadRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		fmt.Println("JSON Decode Error:", err)
		http.Error(w, "Invalid request", http.StatusBadRequest)
		return
	}

	w.Header().Set("Content-Type", "text/csv")
	w.Header().Set("Content-Disposition", `attachment; filename="search_items.csv"`)

	writer := csv.NewWriter(w)
	defer writer.Flush()

	writer.Write([]string{"#", "Name", "URL"})

	name := "google;gmail"
	URL := "https://www.goo;gle.com/"
	for i := 0; i < 5; i++ {
		writer.Write([]string{
			fmt.Sprintf("%d", i+1),
			name,
			URL,
		})
	}
}

The semicolon in name and URL is causing the text to split across multiple cells instead of staying within one. I would prefer not to change any CSV settings in system. If I wrap values in double quotes ("), I should not see them explicitly in the CSV file when opened.

How can I ensure that values containing semicolons remain in the same cell without requiring the user to modify any settings in their CSV viewer?

It seems you are using semicolon as field separator. Try using comma as field separator (I am not sure but I think it is used by default)

Hi, @Belt_Basya :waving_hand:

CSV isn’t a formal specification, so there’s no single right, or authoritative, way to encode a file. That said, there are very popular conventions for encoding options, and the csv package uses those by default: delimit fields with a comma; quote encoding (special) chars with a double quote. The quote char is not configurable, it will always be a double quote.

If the program opening the file was not parsing your generated CSV correctly, that would be because the program used some different settings, like: delimit fields with semicolon; quote encoding chars with… If you have the problem yourself when opening the file in Excel, you might want to verify your import settings, Change the delimiter that is used when importing a text file.

Because there’s no single right way to encode a CSV file, trying to change some settings in your code and solve the problem at hand for you or someone else might not match another person’s CSV parser defaults. I’d leave your code as is and try to spell out to users what they should expect.

You could also give the user the option of picking their delimiter through your HTTP interface, e.g., URL query params, a web UI, or JSON body with config options. Here’s an example that uses the query param delim=:

func main() {
	http.ListenAndServe("localhost:8999", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// parse JSON body

		w.Header().Set("Content-Type", "text/csv")
		w.Header().Set("Content-Disposition", `attachment; filename="search_items.csv"`)

		writer := csv.NewWriter(w)
		defer writer.Flush()

		r.ParseForm()
		if s := strings.TrimSpace(r.FormValue("delim")); s != "" {
			writer.Comma = rune(s[0])
		}

		writer.Write([]string{"#", "Name", "URL"})

		name := "google;gmail"
		URL := "https://www.goo;gle.com/"

		writer.Write([]string{"1", name, URL})
	}))
}
% curl http://localhost:8999
#,Name,URL
1,google;gmail,https://www.goo;gle.com/

% curl 'http://localhost:8999?delim=Z'
#ZNameZURL
1Zgoogle;gmailZhttps://www.goo;gle.com/

% curl 'http://localhost:8999?delim=%3B'
#;Name;URL
1;"google;gmail";"https://www.goo;gle.com/"