Skip to content

Instantly share code, notes, and snippets.

@bemasher
Created April 13, 2012 22:24
Show Gist options
  • Save bemasher/2380516 to your computer and use it in GitHub Desktop.
Save bemasher/2380516 to your computer and use it in GitHub Desktop.
A program for formatting tables copied from excel into the LaTeX tabular environment.
package main
import (
"os"
"fmt"
"math"
"strconv"
"strings"
"errhandler"
"text/template"
)
// Raw data copied from excel is tab delimited
const raw = `F Gain Phase
1.0 -0.8 180.4
10.0 -0.8 180.1
100.0 -0.8 178.9
1000.0 0.0 166.6
10000.0 -21.7 -19.1
100000.0 -47.1 -131.5
1000000.0 -47.6 -105.7
2000.0 2.8 143.8
3000.0 5.0 86.6
4000.0 -0.6 32.5
3870.0 0.0 36.9
2950.0 5.0 90.4
4360.0 -3.0 23.2`
type Table struct {
Headers string
Rows []string
Caption string
ColumnCount int
}
// Returns the alignment parameter based on the number of columns: |c|c| ...
func (t Table) Alignment() string {
return strings.Repeat("|c", t.ColumnCount) + "|"
}
// Returns a list of lines
func SplitLines(i string) []string {
return strings.Split(i, "\n")
}
func ParseTable(i, caption string) Table {
var table Table
// Split into lines
lines := SplitLines(raw)
// Parse the headers and store the caption
table.ParseHeader(lines[0])
table.Caption = caption
// Parse each row
for _, line := range lines[1:] {
table.ParseRow(line)
}
return table
}
func (t *Table) ParseHeader(i string) {
result := make([]string, 0)
// Split on tabs and wrap each word in $'s
// Most of the time, first letter is capitalized,
// rest of the word is subscripted
for _, j := range strings.Split(i, "\t") {
if len(j) == 1 {
result = append(result, fmt.Sprintf("$%s$", j))
} else {
result = append(result, fmt.Sprintf("$%c_{%s}$", j[0], j[1:]))
}
}
// Set the column count for printing the alignment parameter later
t.ColumnCount = len(result)
// Join the headers with the cell separation character &
t.Headers = strings.Join(result, " & ")
}
func (t *Table) ParseRow(i string) {
result := make([]string, 0)
// Split on tabs
for _, j := range strings.Split(i, "\t") {
// Parse the number
temp, err := strconv.ParseFloat(j, 64)
// So long as we didn't encounter an error add it to the list
if err == nil {
// Render the number in engineering notation
result = append(result, EngineeringNotation(float64(temp)))
}
}
// Join all rows with the cell separation character &
t.Rows = append(t.Rows, strings.Join(result, " & "))
}
func EngineeringNotation(i float64) string {
// List of magnitudes for siunitx
order := map[int]string{
-24: "\\yocto", -21: "\\zepto", -18: "\\atto", -15: "\\femto",
-12: "\\pico", -9: "\\nano", -6: "\\micro", -3: "\\milli", 0: "",
3: "\\kilo", 6: "\\mega", 9: "\\giga", 12: "\\tera", 15: "\\peta",
18: "\\exa", 21: "\\zetta", 24: "\\yotta"}
// Round to the nearest integer magnitude
magnitude := int(math.Floor(math.Log10(math.Abs(i)) + 0.5))
// Round to nearest multiple of 3
quantized := (magnitude / 3) * 3
// Would normally generate NaN when rendering to text
if i == 0.0 {
return fmt.Sprintf("$%0.1f\\si{}$", i)
}
return fmt.Sprintf("$%0.1f\\si{%s}$", i * math.Pow10(-quantized), order[quantized])
}
func main() {
// Parse the table with the given caption
table := ParseTable(raw, "Measured gain and phase at various points in the frequency domain.")
// Set the delimiters before parsing, using {{ and }} is
// difficult with LaTeX since it uses { and } in so many places
t := template.New("table.txt").Delims("<<", ">>")
// Parse the table, exit if we couldn't
_, err := t.ParseFiles("table.txt")
errhandler.Handle("Error parsing template: ", err)
// Execute the template with the parsed table
// exit if an error occurs
err = t.Execute(os.Stdout, table)
errhandler.Handle("Error executing template: ", err)
}
\begin{table}[p]
\begin{minipage}[b]{0.5\linewidth}
\centering
\begin{tabular}{|c|c|c|}
\hline
$F$ & $G_{ain}$ & $P_{hase}$ \\
\hline
$1.0\si{}$ & $-0.8\si{}$ & $180.4\si{}$ \\
\hline
$10.0\si{}$ & $-0.8\si{}$ & $180.1\si{}$ \\
\hline
$100.0\si{}$ & $-0.8\si{}$ & $178.9\si{}$ \\
\hline
$1.0\si{\kilo}$ & $0.0\si{}$ & $166.6\si{}$ \\
\hline
$10.0\si{\kilo}$ & $-21.7\si{}$ & $-19.1\si{}$ \\
\hline
$100.0\si{\kilo}$ & $-47.1\si{}$ & $-131.5\si{}$ \\
\hline
$1.0\si{\mega}$ & $-47.6\si{}$ & $-105.7\si{}$ \\
\hline
$2.0\si{\kilo}$ & $2.8\si{}$ & $143.8\si{}$ \\
\hline
$3.0\si{\kilo}$ & $5.0\si{}$ & $86.6\si{}$ \\
\hline
$4.0\si{\kilo}$ & $-0.6\si{}$ & $32.5\si{}$ \\
\hline
$3.9\si{\kilo}$ & $0.0\si{}$ & $36.9\si{}$ \\
\hline
$3.0\si{\kilo}$ & $5.0\si{}$ & $90.4\si{}$ \\
\hline
$4.4\si{\kilo}$ & $-3.0\si{}$ & $23.2\si{}$ \\
\hline
\end{tabular}
\caption{Measured gain and phase at various points in the frequency domain.}
\end{minipage}
\end{table}
\begin{table}[p]
\begin{minipage}[b]{0.5\linewidth}
\centering
\begin{tabular}{<<.Alignment>>}
\hline
<<.Headers>> \\
\hline
<<range .Rows>> <<.>> \\
\hline
<<end>>\end{tabular}
\caption{<<.Caption>>}
\end{minipage}
\end{table}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment