Go语言学习之-带分割符的文件转excel-PLUS版

发布时间 2023-12-15 11:00:29作者: silencemaker1221
package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"github.com/axgle/mahonia"
	"github.com/xuri/excelize/v2"
	"log"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
)

var splitor = ""

var charset = "GBK"

func splt(c rune) bool {
	//c = ''
	if c == rune(splitor[0]) {
		return true
	} else {
		return false
	}
}

func ToAlphaString(value int) string {
	if value < 0 {
		return ""
	}
	var ans string
	i := value + 1
	for i > 0 {
		ans = string((i-1)%26+65) + ans
		i = (i - 1) / 26
	}
	return ans
}

//列表去重
func uniqvalue(vallist []string) (ret []string){
	sort.Strings(vallist)
	vlen:=len(vallist)
	for i := 0; i < vlen; i++ {
		if(i>0 && vallist[i-1]==vallist[i])||len(vallist[i])==0{
			continue
		}
		ret=append(ret,vallist[i])
	}
	return ret
}

func list2sortdig(fldlistidx []string) []int  {
	//flds:=strings.Split(fldlistidx,",")
	flds1:=make([]int, len(fldlistidx))
	for i, fld := range fldlistidx {
		if fv,err:=strconv.ParseInt(fld,10,8);err==nil{
			flds1[i]= int(fv)
		}
	}
	sort.Ints(flds1)
	return flds1
}

func main() {
	var enc mahonia.Decoder

	//filename := "C:\\Users\\Downloads\\11.del"
	//filename = "C:\\soft1\\11.txt"
	var filename string
	var headfile string
	var hsplitor = "|"
	var fldidxlist string
	var top=-1
	if len(os.Args) > 1 {
		flag.ErrHelp = errors.New("")
		flag.StringVar(&filename, "f", "111.dat", "--filename 文件名【可以把文件拖放在此处】")
		flag.StringVar(&splitor, "sp", "\u0003", "--splitor [,  \u0008 ,\u0003, \u0009  ]  分割符,一般为\u0003或者逗号等可见字符单字符")
		flag.StringVar(&headfile, "hf", "111_head.txt", "--headfile 表头文件名【可以把文件拖放在此处,默认以|做分割符】,可以自定义")
		flag.StringVar(&hsplitor, "hs", "|", "--hsplitor [,  \u0008 ,\u0003, \u0009  ]  分割符,一般为\u0003或者逗号等可见字符单字符")
		flag.StringVar(&charset, "c", "GBK", "--charset [,GBK/UTF-8...  ]  字符集编码,一般为GBK")
		flag.StringVar(&fldidxlist, "fdlst", "1", "--字段列表编号,用英文逗号分割,最后用双引号引用,如1,2,3,4,第一列编号为1")
		flag.IntVar(&top, "t", -1, "--top 取前多少个")
		flag.Parse()
		//rune(splitor)
		fmt.Println("filename:" + filename)
		//fmt.Println([]rune(splitor))
		fmt.Printf("%s,%X", "splitor:"+string(splitor)+",hex:", splitor)
		//return
	} else {
		fmt.Println("请输入文件名,可以直接用鼠标点击文件拖入到此(如果有表头文件,请确保同目录下的文件名以_head.txt结束,如1.dat,头文件名为1_head.txt):")
		fmt.Scan(&filename)
	}
	enc = mahonia.NewDecoder(charset)

	if len(filename) < 5 {
		tmpstr := fmt.Sprintf("输入的文件名[%s]长度好像不够5位,是否有问题?", filename)
		panic(errors.New(tmpstr))
	}
	paths, basefile := filepath.Split(filename)
	filesuffix := path.Ext(basefile)
	newbasename := paths + basefile[0:len(basefile)-len(filesuffix)]
	headname:=newbasename+"_head.txt"
	xlsfilename := newbasename + ".xlsx"
	logfilename := newbasename + ".log"

	logFile, err := os.OpenFile(logfilename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		fmt.Println("open log file failed, err:", err)
		return
	}
	log.SetOutput(logFile)
	log.SetFlags(log.Lshortfile | log.Lmicroseconds | log.Ldate)
	log.Printf("日志文件:[%s]\n", logfilename)
	log.Printf("目标文件[%s]\n", xlsfilename)
	fmt.Printf("目标文件路径为:[%s]\n", xlsfilename)
	fmt.Printf("目标日志路径为:[%s]\n", logfilename)
	fp1, err := os.Open(filename)
	if err != nil {
		tmpstr := fmt.Sprintf("%s文件打开错误!", filename)
		log.Println(tmpstr)
		panic(err)
	}
	defer fp1.Close()

	row0 := make([]string, 10,300)

	var headname0=""
	_,err=os.Stat(headname)
	if err==nil{
		headname0=headname
		log.Printf("-----输入的头文件[%s]存在------,以此为解析!\n", headname)
	}
	_, err = os.Stat(headfile)
	if err==nil{
		headname0=headfile
		log.Printf("-----输入的头文件[%s]存在------,以此为解析!\n", headfile)
	}

	if len(headname0)==0{
		log.Printf("-----输入的头文件不存在------!\n")
	}

	fp2, err := os.Open(headname0)
	if err != nil {
		tmpstr := fmt.Sprintf("%s文件打开错误!", headname0)
		log.Println(tmpstr)
	}else{
		log.Printf("-----读取%s------!\n",headname0)
		fs2:=bufio.NewScanner(fp2)
		fs2.Scan()
		splitstrLine:=fs2.Text()

		row0= strings.Split(splitstrLine, hsplitor)
		tmpstr:="{"
		for i, rval := range row0 {
			tmpstr+=strconv.Itoa(i+1)+":"+enc.ConvertString(rval)+","
		}
		tmpstr+=tmpstr+"}"
		log.Printf("-----读取字段%d个,第一个是[%s]------!\n", len(row0),tmpstr)
	}
	defer fp2.Close()

	fs := bufio.NewScanner(fp1)
	//fs1 := bufio.NewScanner(fp2)

	var rowno = 2
	if len(row0)>1{
		rowno = 1
	}
	log.Printf("-----rowno:%d------!\n", rowno)
	//xlsfilename := "1122221.xlsx"
	f := excelize.NewFile()
	f.SaveAs(xlsfilename)

	file1, err := excelize.OpenFile(xlsfilename)
	if err != nil {
		log.Println(err)
	}

	streamWriter, err := file1.NewStreamWriter(enc.ConvertString("Sheet1"))
	if err != nil {
		log.Println(err)
	}

	defer func() {
		if err := recover(); err != nil {
			streamWriter.Flush()
			f.SaveAs(xlsfilename)
			file1.Save()
			log.Printf("recover:%v", err)
		}
	}()

	f.SetActiveSheet(1)
	//f.SetSheetName("Sheet1")
	//f.SetPanes("Sheet1",`{"freeze":true,"split":false,"x_split":0,"y_split":1,"top_left_cell":"A2","actve_pane":"topRight"}`)
	streamWriter.SetPanes( &excelize.Panes{
		Freeze:      true,
		Split:       false,
		XSplit:      0,
		YSplit:      1,
		TopLeftCell: "A2",
		ActivePane:  "bottomLeft",
		Selection: []excelize.Selection{
			{ ActiveCell: "A2", Pane: "bottomLeft"},
		},
	})

	//defer f.SaveAs(xlsfilename)
	//index:=f.NewSheet("data")
	//f.SetActiveSheet(index)
	fldidxlist1:=list2sortdig(uniqvalue(strings.Split(fldidxlist,hsplitor)))
	colcnt:=0
	for fs.Scan() {
		splitstrLine := fs.Text()
		//fildstr := strings.FieldsFunc(splitstrLine, splt)
		fildstr := strings.Split(splitstrLine, splitor)
		//log.Printf("fldidxlist1:[%d] fildstr:[%s]", len(fldidxlist1),fildstr)
		if len(fldidxlist1)>1 {
			colcnt=len(fldidxlist1)
		}else{
			colcnt = len(fildstr)
		}
		//log.Printf("pos:%d-->colcnt:[%d]\n",rowno,colcnt)
		//row := make([]interface{}, colcnt)

		row := make([]interface{}, colcnt)
		if rowno==1 {
			log.Printf("recover:%v", fldidxlist1)
			if len(fldidxlist1)>1{
				colcnt=len(fldidxlist1)
				for i := 0; i < colcnt; i++ {
					row[i]=enc.ConvertString(row0[fldidxlist1[i]-1])
				}
			}else if(len(row0)>1){
				//需要用此语法处理
				for i := 0; i < len(row0) && row0[i]!=""; i++ {
					row[i]=enc.ConvertString(row0[i])
				}
			}
			//log.Printf("-----[%d]第一个是[%s],rowno[%d]------!\n",colcnt,row[:colcnt],rowno)
			cell, _ := excelize.CoordinatesToCellName(1, rowno)
			//log.Printf("----1-")
			if err := streamWriter.SetRow(cell, row[:colcnt]); err != nil {
				log.Println(err)
			}
			//log.Printf("-----")
			row = make([]interface{}, len(row))
			//log.Printf("-----2")
			if top>0{
				top++
			}
			//log.Printf("top:[%d] ",top)
			rowno++
		}

		//log.Printf("colcnt:[%d] ",colcnt)
		//row = make([]interface{}, colcnt)
		for colno := 0; colno < colcnt; colno++ {
			tmpcolno:=colno
			if len(fldidxlist1)>1{
				tmpcolno=fldidxlist1[colno]-1
			}
			//log.Printf("tmpcolno:[%d] ",tmpcolno)
			if fildstr[tmpcolno] != "" {
				row[colno] = enc.ConvertString(fildstr[tmpcolno])
			} else {
				row[colno] = ""
			}
		}
		//log.Println("for over ")
		cell, _ := excelize.CoordinatesToCellName(1, rowno)
		if err := streamWriter.SetRow(cell, row[:colcnt]); err != nil {
			log.Println(err)
		}
		if rowno%30000 == 0 {
			log.Printf("-----已写入[%d]行------\n", rowno)
		}

		if rowno > excelize.TotalRows {
			panic(errors.New("rows number exceeds maximum limit"))
		}

		//if rowno >= 20000 {
		//	break
		//}

		if top>0 && rowno>=top{
			log.Printf("-----已写入[%d]行------\n", rowno)
			break
		}
		rowno++
	}
	if len(row0)>1{
		rowno--
	}
	log.Printf("-----共写入数据[%d]行------\n", rowno)

	if err := streamWriter.Flush(); err != nil {
		log.Println(err)
		//return
	}

	if err := file1.Save(); err != nil {
		log.Println(err)
	}
	//println(fp1)
	log.Println("完成!")
	os.Exit(0)
}

  

操作步骤

附件中的exe文件是可执行文件,

wps中需要另存为一个目录,再使用

word中可以复制,粘贴到一个目录再使用

 

此程序的作用是把原来的dat文件(带分割符的文件即可,数据文件的后缀名可以是其他,如txt)转换成excel的后缀名为xlsx的文件

可以用于大量的数据的转换

 

1双击操作(全表转换)

双击Dat2Xls.exe,拖入程序

如果需要表头,请把表头的名字修改为 _head.txt结尾的文件名

如您要转换的文件为1.dat 表头则为 1_head.txt

 

回车即可

 

2、命令行操作(涉及到高级操作,自定义选择选项)

直接在命令行拖入此程序,可以查看有哪些选项

注意:

只有文件是必须有的

 

运行如图:

 

 

 

帮助如图:

 

 

字符编码一般都是GBK,即需要转换的文件和表头文件都是一个编码格式

cmd下切换到Dat2Xls.exe的程序所在目录运行

如下命令:

简写为: -f后跟的是需要转换为excel的文件名

Dat2Xls.exe  -f E:\myexe\a20210831.dat  -fdlst "1|2|3|4|5|6|7|20|30"

 

解析:f后面为需要解析的文件名,拖入即可显示全文件名,fdlst后面为数据所在列的编号,需要用双引号引起来,中间的列号用|分割,列号从1开始,至少为两列

如果不需要指定列,则不需要-fdlst参数

Dat2Xls.exe  -f E:\myexe\a_20210831.dat

 

 

3查看日志

解析目录下会有一个同名的.log文件为日志文件,可以查看相应日志