9 Commits

Author SHA1 Message Date
60b2f676bd Merge pull request #2 from anthonyaxenov/develop
New v1.2.0: tsv support
2022-02-23 12:29:08 +08:00
13dedbac3b New v1.2.0: tsv support 2022-02-23 12:25:40 +08:00
c7fd26e7b4 Merge pull request #1 from anthonyaxenov/develop
New version
2022-02-17 13:13:48 +08:00
e7f8f3e3ad Update readme 2022-02-17 13:12:11 +08:00
f1aa5d3eff New version
- stdin support
- refactored error handling
- help and some comments in src
2022-02-17 13:11:59 +08:00
bcb0a39d82 Update example.csv for more test data 2022-02-17 13:07:42 +08:00
bdbc7bf16b Update .gitignore for md files 2022-02-17 13:07:22 +08:00
1ff1ae7cd0 Update launch.json to debug in vscode 2022-02-17 13:07:05 +08:00
1dbd735d93 Readme 2022-02-17 11:41:04 +08:00
6 changed files with 229 additions and 61 deletions

2
.gitignore vendored
View File

@@ -1,4 +1,6 @@
/bin
# /.vscode
*.csv
*.tsv
*.md
!example.csv

41
.vscode/launch.json vendored
View File

@@ -2,19 +2,28 @@
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${fileBasename}",
"args": [
"example.csv",
]
}
]
}
}
"version": "0.2.0",
"configurations": [
{
"name": "Debug csv2md",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "main.go",
"args": [
"-f=example.csv",
]
},
{
"name": "Debug csv2md (tsv)",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "main.go",
"args": [
"-t",
"-f=example.tsv",
]
}
]
}

View File

@@ -1,23 +1,64 @@
# 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.
Building:
## Usage
```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
csv2md example.csv > example.md # makes new file
csv2md example.csv | less # view result using *pager*
...anything is possible with redirection and piping
Available `FILE` formats:
* csv (default);
* tsv (with `-t` argument).
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)

View File

@@ -1,14 +1,50 @@
"Month","1958","1959","1960"
"JAN",340,360,417
"FEB",318,342,391
"MAR",362,406,419
"APR",348,396,461
"MAY",363,420,472
"JUN",435,472,535
"JUL",491,548,622
"AUG",505,559,606
"SEP",404,463,508
"OCT",359,407,461
"NOV",310,362,390
"DEC",337,405,432
"Name","Team","Position","Height(inches)","Weight(lbs)","Age"
"Adam Donachie","BAL","Catcher",74,180,22.99
"Paul Bako","BAL","Catcher",74,215,34.69
"Ramon Hernandez","BAL","Catcher",72,210,30.78
"Kevin Millar","BAL","First Baseman",72,210,35.43
"Chris Gomez","BAL","First Baseman",73,188,35.71
"Brian Roberts","BAL","Second Baseman",69,176,29.39
"Miguel Tejada","BAL","Shortstop",69,209,30.77
"Melvin Mora","BAL","Third Baseman",71,200,35.07
"Aubrey Huff","BAL","Third Baseman",76,231,30.19
"Adam Stern","BAL","Outfielder",71,180,27.05
"Jeff Fiorentino","BAL","Outfielder",73,188,23.88
"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
1 Month Name 1958 Team 1959 Position 1960 Height(inches) Weight(lbs) Age
2 JAN Adam Donachie 340 BAL 360 Catcher 417 74 180 22.99
3 FEB Paul Bako 318 BAL 342 Catcher 391 74 215 34.69
4 MAR Ramon Hernandez 362 BAL 406 Catcher 419 72 210 30.78
5 APR Kevin Millar 348 BAL 396 First Baseman 461 72 210 35.43
6 MAY Chris Gomez 363 BAL 420 First Baseman 472 73 188 35.71
7 JUN Brian Roberts 435 BAL 472 Second Baseman 535 69 176 29.39
8 JUL Miguel Tejada 491 BAL 548 Shortstop 622 69 209 30.77
9 AUG Melvin Mora 505 BAL 559 Third Baseman 606 71 200 35.07
10 SEP Aubrey Huff 404 BAL 463 Third Baseman 508 76 231 30.19
11 OCT Adam Stern 359 BAL 407 Outfielder 461 71 180 27.05
12 NOV Jeff Fiorentino 310 BAL 362 Outfielder 390 73 188 23.88
13 DEC Freddie Bynum 337 BAL 405 Outfielder 432 73 180 26.96
14 Nick Markakis BAL Outfielder 74 185 23.29
15 Brandon Fahey BAL Outfielder 74 160 26.11
16 Corey Patterson BAL Outfielder 69 180 27.55
17 Jay Payton BAL Outfielder 70 185 34.27
18 Jay Gibbons BAL Designated Hitter 72 197 30
19 Erik Bedard BAL Starting Pitcher 73 189 27.99
20 Hayden Penn BAL Starting Pitcher 75 185 22.38
21 Adam Loewen BAL Starting Pitcher 78 219 22.89
22 Daniel Cabrera BAL Starting Pitcher 79 230 25.76
23 Steve Trachsel BAL Starting Pitcher 76 205 36.33
24 Jaret Wright BAL Starting Pitcher 74 230 31.17
25 Kris Benson BAL Starting Pitcher 76 195 32.31
26 Scott Williamson BAL Relief Pitcher 72 180 31.03
27 John Parrish BAL Relief Pitcher 71 192 29.26
28 Danys Baez BAL Relief Pitcher 75 225 29.47
29 Chad Bradford BAL Relief Pitcher 77 203 32.46
30 Jamie Walker BAL Relief Pitcher 74 195 35.67
31 Brian Burres BAL Relief Pitcher 73 182 25.89
32 Kurt Birkins BAL Relief Pitcher 74 188 26.55
33 James Hoey BAL Relief Pitcher 78 200 24.17
34 Sendy Rleal BAL Relief Pitcher 73 180 26.69
35 Chris Ray BAL Relief Pitcher 75 200 25.13
36 Jeremy Guthrie BAL Relief Pitcher 73 200 27.9
37 A.J. Pierzynski CWS Catcher 75 245 30.17
38 Toby Hall CWS Catcher 75 240 31.36
39 Paul Konerko CWS First Baseman 74 215 30.99
40 Tadahito Iguchi CWS Second Baseman 69 185 32.24
41 Juan Uribe CWS Shortstop 71 175 27.61
42 Alex Cintron CWS Shortstop 74 199 28.2
43 Joe Crede CWS Third Baseman 73 200 28.85
44 Josh Fields CWS Third Baseman 73 215 24.21
45 Ryan Sweeney CWS Outfielder 76 200 22.02
46 Brian N. Anderson CWS Outfielder 74 205 24.97
47 Luis Terrero CWS Outfielder 74 206 26.78
48 Pablo Ozuna CWS Outfielder 70 186 32.51
49 Scott Podsednik CWS Outfielder 72 188 30.95
50 Jermaine Dye CWS Outfielder 77 220 33.09

6
example.tsv Normal file
View 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
1 impossible political hand larger upward during
2 shade tip opinion star keep aside
3 wrong heat line pool song just
4 she slowly gain snow ourselves six
5 race thrown get yard nearest swam
6 though than teacher away dirt escape

116
main.go
View File

@@ -2,6 +2,8 @@ package main
import (
"encoding/csv"
"errors"
"flag"
"fmt"
"log"
"os"
@@ -9,33 +11,55 @@ import (
"strings"
)
const VERSION = "1.0.0"
const VERSION = "1.2.0"
func main() {
if len(os.Args) < 2 {
usage()
os.Exit(1)
log.SetFlags(0)
filepath := flag.String("f", "", "File path")
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 err != nil {
panic(err)
}
if _, err := os.Stat(src); err != nil {
panic(err)
}
if *filepath == "" {
data, err := readRawCsv(*as_tsv)
if err != nil {
log.Fatal(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 {
fmt.Println(row)
}
}
func usage() {
fmt.Fprintln(os.Stderr, "csv2md v" + VERSION)
fmt.Fprintln(os.Stderr, "Usage: csv2md data.csv > data.md")
}
// ExpandPath return absolute path to file replacing ~ to user's home folder
func ExpandPath(path string) (string, error) {
homepath, err := os.UserHomeDir()
if err != nil {
@@ -45,22 +69,43 @@ func ExpandPath(path string) (string, error) {
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)
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()
csvReader := csv.NewReader(f)
if as_tsv {
csvReader.Comma = '\t'
}
records, err := csvReader.ReadAll()
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 {
var result []string
for idx, row := range records {
@@ -79,3 +124,32 @@ func convert(records [][]string) []string {
}
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)
}
}