Creating a Go-Based Command-Line Tool for CSV Data Manipulation

Table of Contents

  1. Introduction
  2. Prerequisites
  3. Setup
  4. Parsing CSV
  5. Manipulating Data
  6. Writing CSV
  7. Conclusion


Introduction

In this tutorial, we will learn how to create a command-line tool in Go for manipulating CSV (Comma-Separated Values) data. By the end of this tutorial, you’ll be able to parse CSV files, manipulate the data, and write the modified data back to a CSV file.

Prerequisites

To follow along with this tutorial, you should have basic knowledge of Go programming language syntax and concepts. Additionally, you’ll need Go installed on your system. If you haven’t installed Go, you can download it from the official website: golang.org/dl.

Setup

  1. Create a new directory for your project.
  2. Open a terminal or command prompt and navigate to the project directory.

  3. Initialize a new Go module using the following command:

     go mod init github.com/your-username/csv-tool
    

    Replace your-username with your GitHub username or a relevant identifier.

  4. Create a new Go source file named main.go in the project directory.

Parsing CSV

First, we need to parse the CSV file and load its contents into memory. We’ll use the "encoding/csv" package provided by Go’s standard library for this purpose.

  1. Open the main.go file in a text editor.

  2. Import the necessary packages:

     package main
        
     import (
     	"encoding/csv"
     	"fmt"
     	"io"
     	"os"
     )
    
  3. Define a parseCSV function that takes a file path as an argument and returns a two-dimensional slice representing the CSV data:

     func parseCSV(filePath string) ([][]string, error) {
     	file, err := os.Open(filePath)
     	if err != nil {
     		return nil, err
     	}
     	defer file.Close()
        
     	r := csv.NewReader(file)
     	data, err := r.ReadAll()
     	if err != nil {
     		return nil, err
     	}
        
     	return data, nil
     }
    
  4. In the main function, call the parseCSV function with the path to your CSV file:

     func main() {
     	data, err := parseCSV("path/to/your/csv/file.csv")
     	if err != nil {
     		fmt.Println("Error:", err)
     		return
     	}
        
     	fmt.Println("CSV Data:", data)
     }
    

    Replace "path/to/your/csv/file.csv" with the actual path to your CSV file.

  5. Save the main.go file and run the program using the following command:

     go run main.go
    

    If everything works correctly, you should see the parsed CSV data printed on the console.

Manipulating Data

Once we have the CSV data in memory, we can manipulate it as per our requirements. For the sake of this tutorial, let’s assume we want to add a new column called “Total” to calculate the sum of two existing columns.

  1. Modify the parseCSV function to return a [][]string slice for both the original and modified CSV data:

     func parseCSV(filePath string) ([][]string, [][]string, error) {
     	file, err := os.Open(filePath)
     	if err != nil {
     		return nil, nil, err
     	}
     	defer file.Close()
        
     	r := csv.NewReader(file)
     	data, err := r.ReadAll()
     	if err != nil {
     		return nil, nil, err
     	}
        
     	return data, nil, nil
     }
    
  2. Add a new helper function called calculateTotal that takes the original CSV data and returns the modified CSV data with the “Total” column added:

     func calculateTotal(originalData [][]string) [][]string {
     	modifiedData := make([][]string, len(originalData))
     	copy(modifiedData, originalData)
        
     	for i := 0; i < len(modifiedData); i++ {
     		row := modifiedData[i]
     		column1, _ := strconv.Atoi(row[1])
     		column2, _ := strconv.Atoi(row[2])
     		total := column1 + column2
     		row = append(row, strconv.Itoa(total))
     		modifiedData[i] = row
     	}
        
     	return modifiedData
     }
    
  3. Modify the main function to use the calculateTotal function:

     func main() {
     	originalData, err := parseCSV("path/to/your/csv/file.csv")
     	if err != nil {
     		fmt.Println("Error:", err)
     		return
     	}
        
     	modifiedData := calculateTotal(originalData)
        
     	fmt.Println("Original Data:", originalData)
     	fmt.Println("Modified Data:", modifiedData)
     }
    
  4. Save the main.go file and run the program again using the following command:

     go run main.go
    

    You should now see both the original and modified CSV data printed on the console.

Writing CSV

Finally, we need to write the modified CSV data back to a file. We’ll leverage the same "encoding/csv" package for this task.

  1. Modify the parseCSV function to return both the original and modified CSV file paths:

     func parseCSV(filePath string) ([][]string, [][]string, string, string, error) {
     	// ...
     	return data, modifiedData, filePath, modifiedFilePath, nil
     }
    
  2. Implement a new helper function called writeCSV that takes a file path and CSV data as arguments and writes the data to the file:

     func writeCSV(filePath string, data [][]string) error {
     	file, err := os.Create(filePath)
     	if err != nil {
     		return err
     	}
     	defer file.Close()
        
     	w := csv.NewWriter(file)
     	err = w.WriteAll(data)
     	if err != nil {
     		return err
     	}
        
     	return nil
     }
    
  3. Modify the main function to call the writeCSV function:

     func main() {
     	originalData, modifiedData, filePath, modifiedFilePath, err := parseCSV("path/to/your/csv/file.csv")
     	if err != nil {
     		fmt.Println("Error:", err)
     		return
     	}
        
     	modifiedFilePath := filePath[:len(filePath)-4] + "_modified.csv"
     	err = writeCSV(modifiedFilePath, modifiedData)
     	if err != nil {
     		fmt.Println("Error:", err)
     		return
     	}
        
     	fmt.Println("Modified CSV file created:", modifiedFilePath)
     }
    
  4. Save the main.go file and run the program once more using the following command:

     go run main.go
    

    If everything goes well, a new CSV file with the modified data should be created in the same directory as the original file.

Conclusion

Congratulations! You have successfully created a Go-based command-line tool for CSV data manipulation. In this tutorial, you learned how to parse CSV files, manipulate the data, and write the modified data back to a file. This tool can be further extended to support more advanced operations on CSV data.

By understanding the concepts covered in this tutorial, you’ll be well-equipped to handle CSV data manipulation tasks in your Go projects.

Remember to explore Go’s standard library documentation for additional features and packages that can further enhance your command-line tool. Happy coding!


I hope you find this tutorial helpful. If you have any questions, please feel free to ask.