Compare commits
9 Commits
d7ffa94db5
...
v1.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 60b2f676bd | |||
|
13dedbac3b
|
|||
| c7fd26e7b4 | |||
|
e7f8f3e3ad
|
|||
|
f1aa5d3eff
|
|||
|
bcb0a39d82
|
|||
|
bdbc7bf16b
|
|||
|
1ff1ae7cd0
|
|||
|
1dbd735d93
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,6 @@
|
|||||||
/bin
|
/bin
|
||||||
# /.vscode
|
# /.vscode
|
||||||
*.csv
|
*.csv
|
||||||
|
*.tsv
|
||||||
|
*.md
|
||||||
!example.csv
|
!example.csv
|
||||||
|
|||||||
41
.vscode/launch.json
vendored
41
.vscode/launch.json
vendored
@@ -2,19 +2,28 @@
|
|||||||
// Use IntelliSense to learn about possible attributes.
|
// Use IntelliSense to learn about possible attributes.
|
||||||
// Hover to view descriptions of existing attributes.
|
// Hover to view descriptions of existing attributes.
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
{
|
"version": "0.2.0",
|
||||||
"version": "0.2.0",
|
"configurations": [
|
||||||
"configurations": [
|
{
|
||||||
{
|
"name": "Debug csv2md",
|
||||||
"name": "Launch Package",
|
"type": "go",
|
||||||
"type": "go",
|
"request": "launch",
|
||||||
"request": "launch",
|
"mode": "auto",
|
||||||
"mode": "auto",
|
"program": "main.go",
|
||||||
"program": "${fileBasename}",
|
"args": [
|
||||||
"args": [
|
"-f=example.csv",
|
||||||
"example.csv",
|
]
|
||||||
]
|
},
|
||||||
}
|
{
|
||||||
]
|
"name": "Debug csv2md (tsv)",
|
||||||
}
|
"type": "go",
|
||||||
}
|
"request": "launch",
|
||||||
|
"mode": "auto",
|
||||||
|
"program": "main.go",
|
||||||
|
"args": [
|
||||||
|
"-t",
|
||||||
|
"-f=example.tsv",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
61
README.md
61
README.md
@@ -1,23 +1,64 @@
|
|||||||
# csv2md
|
# csv2md
|
||||||
|
|
||||||
Stupidly simple tool to convert csv-file to [markdown](https://spec-md.com/) table.
|
Stupidly simple tool to convert csv/tsv to [markdown](https://spec-md.com/) table.
|
||||||
|
|
||||||
Outputs result in stdout.
|
Outputs result in stdout.
|
||||||
|
|
||||||
Building:
|
## Usage
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
make help
|
csv2md [-help|--help] [-t] [-f <FILE>]
|
||||||
```
|
```
|
||||||
|
|
||||||
Usage:
|
Available arguments:
|
||||||
|
* `-help` or `--help` - get help
|
||||||
|
* `-f=<FILE>` or `-f <FILE>` - convert specified `FILE`
|
||||||
|
* `-t` - convert input as tsv
|
||||||
|
|
||||||
```shell
|
Available `FILE` formats:
|
||||||
csv2md example.csv > example.md # makes new file
|
* csv (default);
|
||||||
csv2md example.csv | less # view result using *pager*
|
* tsv (with `-t` argument).
|
||||||
...anything is possible with redirection and piping
|
|
||||||
|
Path to `FILE` may be presented as:
|
||||||
|
* absolute path;
|
||||||
|
* path relative to current working directory;
|
||||||
|
* path relative to user home directory (~).
|
||||||
|
|
||||||
|
Also if `PATH` contains whitespaces then you should double-quote it.
|
||||||
|
|
||||||
|
To save result as separate file you can use piping.
|
||||||
|
|
||||||
|
> **IMPORTANT:**
|
||||||
|
> 1. Input data must be valid csv/tsv
|
||||||
|
> 2. Whitespaces allowed only between double-quotes
|
||||||
|
> 3. Due to markdown spec first line of result table will always be presented as header.
|
||||||
|
> So if your raw data hasn't one you'll should add it before conversion or edit later in ready md.
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
csv2md - paste or type csv to stdin and then
|
||||||
|
press Ctrl+D to view result in stdout
|
||||||
|
csv2md -t > example.md - paste or type tsv to stdin and then
|
||||||
|
press Ctrl+D to write result in new file
|
||||||
|
csv2md -f example.csv - convert csv from file and view result in stdout
|
||||||
|
csv2md -t < example.tsv - convert tsv from stdin and view result in stdout
|
||||||
|
csv2md -t < example.tsv > example.md - convert tsv from stdin and write result in new file
|
||||||
|
cat example.csv | csv2md - convert csv from stdin and view result in stdout
|
||||||
|
csv2md -t -f=example.tsv > example.md - convert tsv from file and write result in new file
|
||||||
|
csv2md -f example.csv | less - convert csv from file and view result in stdout using pager
|
||||||
|
|
||||||
|
...anything is possible with redirection and piping, e.g. grep, sed, awk, etc.
|
||||||
```
|
```
|
||||||
|
|
||||||
> **IMPORTANT:** input must be valid csv and whitespaces are allowed only between double-quotes.
|
You can generate some examples here: [csv](https://onlinerandomtools.com/generate-random-csv), [tsv](https://onlinerandomtools.com/generate-random-tsv)
|
||||||
|
|
||||||
Examples can be found here: https://people.sc.fsu.edu/~jburkardt/data/csv/csv.html
|
## Compilation
|
||||||
|
|
||||||
|
1) [Install go](https://go.dev/learn/).
|
||||||
|
2) Download this repo via zip or `git clone`.
|
||||||
|
3) Run `make help` to get help about compilation or `go run . [ARGS...]` to build and run temporary binary.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT](LICENSE)
|
||||||
|
|||||||
64
example.csv
64
example.csv
@@ -1,14 +1,50 @@
|
|||||||
"Month","1958","1959","1960"
|
"Name","Team","Position","Height(inches)","Weight(lbs)","Age"
|
||||||
"JAN",340,360,417
|
"Adam Donachie","BAL","Catcher",74,180,22.99
|
||||||
"FEB",318,342,391
|
"Paul Bako","BAL","Catcher",74,215,34.69
|
||||||
"MAR",362,406,419
|
"Ramon Hernandez","BAL","Catcher",72,210,30.78
|
||||||
"APR",348,396,461
|
"Kevin Millar","BAL","First Baseman",72,210,35.43
|
||||||
"MAY",363,420,472
|
"Chris Gomez","BAL","First Baseman",73,188,35.71
|
||||||
"JUN",435,472,535
|
"Brian Roberts","BAL","Second Baseman",69,176,29.39
|
||||||
"JUL",491,548,622
|
"Miguel Tejada","BAL","Shortstop",69,209,30.77
|
||||||
"AUG",505,559,606
|
"Melvin Mora","BAL","Third Baseman",71,200,35.07
|
||||||
"SEP",404,463,508
|
"Aubrey Huff","BAL","Third Baseman",76,231,30.19
|
||||||
"OCT",359,407,461
|
"Adam Stern","BAL","Outfielder",71,180,27.05
|
||||||
"NOV",310,362,390
|
"Jeff Fiorentino","BAL","Outfielder",73,188,23.88
|
||||||
"DEC",337,405,432
|
"Freddie Bynum","BAL","Outfielder",73,180,26.96
|
||||||
|
"Nick Markakis","BAL","Outfielder",74,185,23.29
|
||||||
|
"Brandon Fahey","BAL","Outfielder",74,160,26.11
|
||||||
|
"Corey Patterson","BAL","Outfielder",69,180,27.55
|
||||||
|
"Jay Payton","BAL","Outfielder",70,185,34.27
|
||||||
|
"Jay Gibbons","BAL","Designated Hitter",72,197,30
|
||||||
|
"Erik Bedard","BAL","Starting Pitcher",73,189,27.99
|
||||||
|
"Hayden Penn","BAL","Starting Pitcher",75,185,22.38
|
||||||
|
"Adam Loewen","BAL","Starting Pitcher",78,219,22.89
|
||||||
|
"Daniel Cabrera","BAL","Starting Pitcher",79,230,25.76
|
||||||
|
"Steve Trachsel","BAL","Starting Pitcher",76,205,36.33
|
||||||
|
"Jaret Wright","BAL","Starting Pitcher",74,230,31.17
|
||||||
|
"Kris Benson","BAL","Starting Pitcher",76,195,32.31
|
||||||
|
"Scott Williamson","BAL","Relief Pitcher",72,180,31.03
|
||||||
|
"John Parrish","BAL","Relief Pitcher",71,192,29.26
|
||||||
|
"Danys Baez","BAL","Relief Pitcher",75,225,29.47
|
||||||
|
"Chad Bradford","BAL","Relief Pitcher",77,203,32.46
|
||||||
|
"Jamie Walker","BAL","Relief Pitcher",74,195,35.67
|
||||||
|
"Brian Burres","BAL","Relief Pitcher",73,182,25.89
|
||||||
|
"Kurt Birkins","BAL","Relief Pitcher",74,188,26.55
|
||||||
|
"James Hoey","BAL","Relief Pitcher",78,200,24.17
|
||||||
|
"Sendy Rleal","BAL","Relief Pitcher",73,180,26.69
|
||||||
|
"Chris Ray","BAL","Relief Pitcher",75,200,25.13
|
||||||
|
"Jeremy Guthrie","BAL","Relief Pitcher",73,200,27.9
|
||||||
|
"A.J. Pierzynski","CWS","Catcher",75,245,30.17
|
||||||
|
"Toby Hall","CWS","Catcher",75,240,31.36
|
||||||
|
"Paul Konerko","CWS","First Baseman",74,215,30.99
|
||||||
|
"Tadahito Iguchi","CWS","Second Baseman",69,185,32.24
|
||||||
|
"Juan Uribe","CWS","Shortstop",71,175,27.61
|
||||||
|
"Alex Cintron","CWS","Shortstop",74,199,28.2
|
||||||
|
"Joe Crede","CWS","Third Baseman",73,200,28.85
|
||||||
|
"Josh Fields","CWS","Third Baseman",73,215,24.21
|
||||||
|
"Ryan Sweeney","CWS","Outfielder",76,200,22.02
|
||||||
|
"Brian N. Anderson","CWS","Outfielder",74,205,24.97
|
||||||
|
"Luis Terrero","CWS","Outfielder",74,206,26.78
|
||||||
|
"Pablo Ozuna","CWS","Outfielder",70,186,32.51
|
||||||
|
"Scott Podsednik","CWS","Outfielder",72,188,30.95
|
||||||
|
"Jermaine Dye","CWS","Outfielder",77,220,33.09
|
||||||
|
|||||||
|
6
example.tsv
Normal file
6
example.tsv
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
impossible political hand larger upward during
|
||||||
|
shade tip opinion star keep aside
|
||||||
|
wrong heat line pool song just
|
||||||
|
she slowly gain snow ourselves six
|
||||||
|
race thrown get yard nearest swam
|
||||||
|
though than teacher away dirt escape
|
||||||
|
116
main.go
116
main.go
@@ -2,6 +2,8 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
@@ -9,33 +11,55 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const VERSION = "1.0.0"
|
const VERSION = "1.2.0"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if len(os.Args) < 2 {
|
log.SetFlags(0)
|
||||||
usage()
|
filepath := flag.String("f", "", "File path")
|
||||||
os.Exit(1)
|
is_help := flag.Bool("help", false, "Get help")
|
||||||
|
as_tsv := flag.Bool("t", false, "Parse as tsv")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *is_help {
|
||||||
|
usage(os.Stdout)
|
||||||
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
src, err := ExpandPath(os.Args[1])
|
if *filepath == "" {
|
||||||
if err != nil {
|
data, err := readRawCsv(*as_tsv)
|
||||||
panic(err)
|
if err != nil {
|
||||||
}
|
log.Fatal(err)
|
||||||
if _, err := os.Stat(src); err != nil {
|
}
|
||||||
panic(err)
|
print(data)
|
||||||
}
|
} else {
|
||||||
|
src, err := ExpandPath(*filepath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(src); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
result := convert(readCsv(src))
|
data, err := readCsvFile(src, *as_tsv)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
print(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// print write converted data to stdout
|
||||||
|
func print(data [][]string) {
|
||||||
|
if len(data) == 0 {
|
||||||
|
usage(os.Stderr)
|
||||||
|
}
|
||||||
|
result := convert(data)
|
||||||
for _, row := range result {
|
for _, row := range result {
|
||||||
fmt.Println(row)
|
fmt.Println(row)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func usage() {
|
// ExpandPath return absolute path to file replacing ~ to user's home folder
|
||||||
fmt.Fprintln(os.Stderr, "csv2md v" + VERSION)
|
|
||||||
fmt.Fprintln(os.Stderr, "Usage: csv2md data.csv > data.md")
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExpandPath(path string) (string, error) {
|
func ExpandPath(path string) (string, error) {
|
||||||
homepath, err := os.UserHomeDir()
|
homepath, err := os.UserHomeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -45,22 +69,43 @@ func ExpandPath(path string) (string, error) {
|
|||||||
return newpath, err
|
return newpath, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func readCsv(filePath string) [][]string {
|
// readRawCsv read data from file
|
||||||
|
func readCsvFile(filePath string, as_tsv bool) ([][]string, error) {
|
||||||
f, err := os.Open(filePath)
|
f, err := os.Open(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Unable to read input file " + filePath, err)
|
return nil, errors.New("Failed to open file '" + filePath + "': " + err.Error())
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
csvReader := csv.NewReader(f)
|
csvReader := csv.NewReader(f)
|
||||||
|
if as_tsv {
|
||||||
|
csvReader.Comma = '\t'
|
||||||
|
}
|
||||||
|
|
||||||
records, err := csvReader.ReadAll()
|
records, err := csvReader.ReadAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Unable to parse file as CSV for " + filePath, err)
|
return nil, errors.New("Failed to parse file '" + filePath + "': " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return records
|
return records, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readRawCsv read data from stdin
|
||||||
|
func readRawCsv(as_tsv bool) ([][]string, error) {
|
||||||
|
csvReader := csv.NewReader(os.Stdin)
|
||||||
|
if as_tsv {
|
||||||
|
csvReader.Comma = '\t'
|
||||||
|
}
|
||||||
|
|
||||||
|
records, err := csvReader.ReadAll()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("Failed to parse input from stdin: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return records, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert format data from file or stdin as markdown
|
||||||
func convert(records [][]string) []string {
|
func convert(records [][]string) []string {
|
||||||
var result []string
|
var result []string
|
||||||
for idx, row := range records {
|
for idx, row := range records {
|
||||||
@@ -79,3 +124,32 @@ func convert(records [][]string) []string {
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// usage print help into writer
|
||||||
|
func usage(writer *os.File) {
|
||||||
|
usage := []string{
|
||||||
|
"csv2md v" + VERSION,
|
||||||
|
"Anthony Axenov (c) 2022, MIT License",
|
||||||
|
"https://github.com/anthonyaxenov/csv2md",
|
||||||
|
"",
|
||||||
|
"Usage:",
|
||||||
|
"\tcsv2md [-help|--help] [-t] [-f <FILE>]",
|
||||||
|
"",
|
||||||
|
"Available arguments:",
|
||||||
|
"\t-help|--help - get this help",
|
||||||
|
"\t-f=<FILE>|-f <FILE> - convert specified FILE",
|
||||||
|
"\t-t - convert input as TSV",
|
||||||
|
"",
|
||||||
|
"FILE formats supported:",
|
||||||
|
"\t- csv (default)",
|
||||||
|
"\t- tsv (with -t flag)",
|
||||||
|
"",
|
||||||
|
"Path to FILE may be presented as:",
|
||||||
|
"\t- absolute",
|
||||||
|
"\t- relative to current working directory",
|
||||||
|
"\t- relative to user home directory (~)",
|
||||||
|
}
|
||||||
|
for _, str := range usage {
|
||||||
|
fmt.Fprintln(writer, str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user