不同包的Golang函数泛化
Golang function generalization for different packages
假设需要使用这些函数,我怎样才能使这个调用通用,这样我就不会重复几乎相同的代码。
和"encoding/csv"
func getDataFromCSVFiles(files []string) (error, Data) {
data := Data{}
for _, file := range files {
f, err := os.Open(file)
if err != nil {
panic(err)
return err, data
}
defer f.Close()
r := charmap.ISO8859_1.NewDecoder().Reader(f)
reader := csv.NewReader(r)
for i := 1;;i++ {
rec, err := reader.Read()
if i == 1 {
//Skipping header
continue
}
if err != nil {
if err == io.EOF {
break
}
//TODO log error line and csv filename
log.Fatal(err)
}
addWorkbook(rec, &data)
}
}
return nil, data
}
并与
import fw "github.com/hduplooy/gofixedwidth" 除了调用 fw.NewReader
几乎是一样的
func getDataFromPRNFiles(files []string) (error, Data) {
data := Data{}
for _, file := range files {
f, err := os.Open(file)
if err != nil {
panic(err)
return err, data
}
defer f.Close()
r := charmap.ISO8859_1.NewDecoder().Reader(f)
reader := fw.NewReader(r)
for i := 1;;i++ {
rec, err := reader.Read()
if i == 1 {
//Skipping header
continue
}
if err != nil {
if err == io.EOF {
break
}
//TODO log error line and csv filename
log.Fatal(err)
}
addWorkbook(rec, &data)
}
}
return nil, data
}
唯一明显的区别是:
reader := csv.NewReader(r)
对比:
reader := fw.NewReader(r)
我不确定 fw
是什么,但大概两个读者都实现了一个通用接口:
type StringSliceReader interface {
Read() ([]string, error)
}
因此您可以将开场符(csv.NewReader
和 fw.NewReader
)作为函数参数传递:
func getDataFromFiles(files []string, func(r io.Reader) StringArrayReader) (error, Data) {
//...
}
但您需要将它们包装在小函数中以绕过 return 类型:
func newCSVReader(r io.Reader) StringSliceReader {
return csv.NewReader(r)
}
func newFWReader(r io.Reader) StringSliceReader {
return fw.NewReader(r)
}
此外,defer
将要在 函数 退出时执行的事情排队,而不是在循环的下一次迭代时执行。所以如果你这样做:
for _, file := range files {
f, err := os.Open(file)
if err != nil {
panic(err)
return err, data
}
defer f.Close()
//...
}
和 files
有一百个条目,那么在关闭任何文件之前,您将有一百个打开的文件。您可能想将该循环体移动到一个单独的函数中,这样您一次只能打开一个文件。
此外,error
通常是函数的最后一个 return 值,因此您应该 return data, err
更加地道。
结果可能如下所示:
type StringSliceReader interface {
Read() ([]string, error)
}
type NewReader func(r io.Reader) StringSliceReader
func newCSVReader(r io.Reader) StringSliceReader {
return csv.NewReader(r)
}
func newFWReader(r io.Reader) StringSliceReader {
return fw.NewReader(r)
}
func getDataFrom(file string, data *Data, newReader NewReader) error {
f, err := os.Open(file)
if err != nil {
return err
}
defer f.Close()
r := charmap.ISO8859_1.NewDecoder().Reader(f)
reader := newReader(r)
for i := 1; ; i++ {
rec, err := reader.Read()
if i == 1 {
continue
}
if err != nil {
if err == io.EOF {
break
}
log.Fatal(err)
}
addWorkbook(rec, data)
}
return nil
}
func getDataFromFiles(files []string, newReader NewReader) (Data, error) {
data := Data{}
for _, file := range files {
err := getDataFrom(file, newReader, &data)
if err != nil {
panic(err)
return data, err
}
}
return data, nil
}
你可以说 getDataFromFiles(files, newCSVReader)
来读取 CSV 或 getDataFromFiles(files, newFWReader)
来读取 FW 文件。如果你想从其他东西中读取,你只需要一个 NewReader
函数和实现 StringSliceReader
接口的东西。
您可能想要 bury/hide NewReader
函数中的 charmap.ISO8859_1.NewDecoder().Reader(f)
内容,以便更容易阅读 non-Latin-1 编码的文件。您还可以将 newReader NewReader
替换为 getDataFromFiles
中的 map[string]NewReader
,并根据文件的扩展名或其他格式标识符选择要使用的 NewReader
。
假设需要使用这些函数,我怎样才能使这个调用通用,这样我就不会重复几乎相同的代码。
和"encoding/csv"
func getDataFromCSVFiles(files []string) (error, Data) {
data := Data{}
for _, file := range files {
f, err := os.Open(file)
if err != nil {
panic(err)
return err, data
}
defer f.Close()
r := charmap.ISO8859_1.NewDecoder().Reader(f)
reader := csv.NewReader(r)
for i := 1;;i++ {
rec, err := reader.Read()
if i == 1 {
//Skipping header
continue
}
if err != nil {
if err == io.EOF {
break
}
//TODO log error line and csv filename
log.Fatal(err)
}
addWorkbook(rec, &data)
}
}
return nil, data
}
并与 import fw "github.com/hduplooy/gofixedwidth" 除了调用 fw.NewReader
几乎是一样的func getDataFromPRNFiles(files []string) (error, Data) {
data := Data{}
for _, file := range files {
f, err := os.Open(file)
if err != nil {
panic(err)
return err, data
}
defer f.Close()
r := charmap.ISO8859_1.NewDecoder().Reader(f)
reader := fw.NewReader(r)
for i := 1;;i++ {
rec, err := reader.Read()
if i == 1 {
//Skipping header
continue
}
if err != nil {
if err == io.EOF {
break
}
//TODO log error line and csv filename
log.Fatal(err)
}
addWorkbook(rec, &data)
}
}
return nil, data
}
唯一明显的区别是:
reader := csv.NewReader(r)
对比:
reader := fw.NewReader(r)
我不确定 fw
是什么,但大概两个读者都实现了一个通用接口:
type StringSliceReader interface {
Read() ([]string, error)
}
因此您可以将开场符(csv.NewReader
和 fw.NewReader
)作为函数参数传递:
func getDataFromFiles(files []string, func(r io.Reader) StringArrayReader) (error, Data) {
//...
}
但您需要将它们包装在小函数中以绕过 return 类型:
func newCSVReader(r io.Reader) StringSliceReader {
return csv.NewReader(r)
}
func newFWReader(r io.Reader) StringSliceReader {
return fw.NewReader(r)
}
此外,defer
将要在 函数 退出时执行的事情排队,而不是在循环的下一次迭代时执行。所以如果你这样做:
for _, file := range files {
f, err := os.Open(file)
if err != nil {
panic(err)
return err, data
}
defer f.Close()
//...
}
和 files
有一百个条目,那么在关闭任何文件之前,您将有一百个打开的文件。您可能想将该循环体移动到一个单独的函数中,这样您一次只能打开一个文件。
此外,error
通常是函数的最后一个 return 值,因此您应该 return data, err
更加地道。
结果可能如下所示:
type StringSliceReader interface {
Read() ([]string, error)
}
type NewReader func(r io.Reader) StringSliceReader
func newCSVReader(r io.Reader) StringSliceReader {
return csv.NewReader(r)
}
func newFWReader(r io.Reader) StringSliceReader {
return fw.NewReader(r)
}
func getDataFrom(file string, data *Data, newReader NewReader) error {
f, err := os.Open(file)
if err != nil {
return err
}
defer f.Close()
r := charmap.ISO8859_1.NewDecoder().Reader(f)
reader := newReader(r)
for i := 1; ; i++ {
rec, err := reader.Read()
if i == 1 {
continue
}
if err != nil {
if err == io.EOF {
break
}
log.Fatal(err)
}
addWorkbook(rec, data)
}
return nil
}
func getDataFromFiles(files []string, newReader NewReader) (Data, error) {
data := Data{}
for _, file := range files {
err := getDataFrom(file, newReader, &data)
if err != nil {
panic(err)
return data, err
}
}
return data, nil
}
你可以说 getDataFromFiles(files, newCSVReader)
来读取 CSV 或 getDataFromFiles(files, newFWReader)
来读取 FW 文件。如果你想从其他东西中读取,你只需要一个 NewReader
函数和实现 StringSliceReader
接口的东西。
您可能想要 bury/hide NewReader
函数中的 charmap.ISO8859_1.NewDecoder().Reader(f)
内容,以便更容易阅读 non-Latin-1 编码的文件。您还可以将 newReader NewReader
替换为 getDataFromFiles
中的 map[string]NewReader
,并根据文件的扩展名或其他格式标识符选择要使用的 NewReader
。