func DetectEmerHeaders(hdrs []string) bool {
	for _, hd := range hdrs {
		if hd == "" {
			continue
		}
		if hd == "_H:" {
			return true
		}
		if _, ok := EmerHdrCharToType[hd[0]]; !ok {  
			return false
		}
	}
	return true
}
DetectEmerHeaders looks for emergent header special characters -- returns true if found
package etable
import (
	"bufio"
	"encoding/csv"
	"fmt"
	"io"
	"log"
	"math"
	"os"
	"strconv"
	"strings"
	"github.com/emer/etable/etensor"
	"github.com/goki/gi/gi"
	"github.com/goki/ki/kit"
)
type Delims int32
var KiT_Delims = kit.Enums.AddEnum(DelimsN, kit.NotBitFlag, nil)
func (ev Delims) MarshalJSON() ([]byte, error)  { return kit.EnumMarshalJSON(ev) }
func (ev *Delims) UnmarshalJSON(b []byte) error { return kit.EnumUnmarshalJSON(ev, b) }
const (
	Tab Delims = iota
	Comma
	Space
	Detect
	DelimsN
)
func (dl Delims) Rune() rune {
	switch dl {
	case Tab:
		return '\t'
	case Comma:
		return ','
	case Space:
		return ' '
	}
	return '\t'
}
const (
	Headers = true
	NoHeaders = false
)
func (dt *Table) SaveCSV(filename gi.FileName, delim Delims, headers bool) error {
	fp, err := os.Create(string(filename))
	defer fp.Close()
	if err != nil {
		log.Println(err)
		return err
	}
	bw := bufio.NewWriter(fp)
	err = dt.WriteCSV(bw, delim, headers)
	bw.Flush()
	return err
}
func (ix *IdxView) SaveCSV(filename gi.FileName, delim Delims, headers bool) error {
	fp, err := os.Create(string(filename))
	defer fp.Close()
	if err != nil {
		log.Println(err)
		return err
	}
	bw := bufio.NewWriter(fp)
	err = ix.WriteCSV(bw, delim, headers)
	bw.Flush()
	return err
}
func (dt *Table) OpenCSV(filename gi.FileName, delim Delims) error {
	fp, err := os.Open(string(filename))
	defer fp.Close()
	if err != nil {
		log.Println(err)
		return err
	}
	return dt.ReadCSV(bufio.NewReader(fp), delim)
}
func (ix *IdxView) OpenCSV(filename gi.FileName, delim Delims) error {
	fp, err := os.Open(string(filename))
	defer fp.Close()
	if err != nil {
		log.Println(err)
		return err
	}
	err = ix.Table.ReadCSV(bufio.NewReader(fp), delim)
	ix.Sequential()
	return err
}
func (dt *Table) ReadCSV(r io.Reader, delim Delims) error {
	cr := csv.NewReader(r)
	cr.Comma = delim.Rune()
	rec, err := cr.ReadAll()  
	if err != nil || len(rec) == 0 {
		return err
	}
	rows := len(rec)
	strow := 0
	if dt.NumCols() == 0 || DetectEmerHeaders(rec[0]) {
		sc, err := SchemaFromHeaders(rec[0], rec)
		if err != nil {
			log.Println(err.Error())
			return err
		}
		strow++
		rows--
		dt.SetFromSchema(sc, rows)
	}
	dt.SetNumRows(rows)
	for ri := 0; ri < rows; ri++ {
		dt.ReadCSVRow(rec[ri+strow], ri)
	}
	return nil
}
func (dt *Table) ReadCSVRow(rec []string, row int) {
	tc := dt.NumCols()
	ci := 0
	if rec[0] == "_D:" {  
		ci++
	}
	nan := math.NaN()
	for j := 0; j < tc; j++ {
		tsr := dt.Cols[j]
		_, csz := tsr.RowCellSize()
		stoff := row * csz
		for cc := 0; cc < csz; cc++ {
			str := rec[ci]
			if tsr.DataType() != etensor.STRING {
				if str == "" || str == "NaN" || str == "-NaN" || str == "Inf" || str == "-Inf" {
					tsr.SetNull1D(stoff+cc, true)  
					tsr.SetFloat1D(stoff+cc, nan)
				} else {
					tsr.SetString1D(stoff+cc, str)
				}
			} else {
				tsr.SetString1D(stoff+cc, str)
			}
			ci++
			if ci >= len(rec) {
				return
			}
		}
	}
}
func SchemaFromHeaders(hdrs []string, rec [][]string) (Schema, error) {
	if DetectEmerHeaders(hdrs) {
		return SchemaFromEmerHeaders(hdrs)
	}
	return SchemaFromPlainHeaders(hdrs, rec)
}