GO模板中使用函数

目录

数据路径查找工具 nwpc-oper/nwpc-data-client 配置文件中文件名和路径名使用 GO 语言内置模板库 text/template 进行变量替换。

变量替换

例如下面是 modelvar 二进制文件名的模板,其中 .Year.Month.Day.Hour.Forecast 表示需要替换的变量。

注:text/template 默认的模板元素分隔符是 {{ }},项目通过 Delims 函数修改为 { }

file_name: modelvar{.Year}{.Month}{.Day}{.Hour}_{.Forecast}

预设变量

GRAPES GFS 模式中文件保存为整点时次减 3 小时,即为同化时间窗起始时间。 当前业务系统使用的 nwpc-data-client 版本采用定义预设变量的方式支持 -3 小时时间。

下面路径模板中 .Year4DV.Month4DV.Day4DV.Hour4DV 是预设变量。

path: /g1/COMMONDATA/OPER/NWPC/GRAPES_GFS_GDA/Prod-grib/{.Year4DV}{.Month4DV}{.Day4DV}{.Hour4DV}/ORIG

预设变量在程序内部预先生成,下面代码包含两组预设变量:

  • 4DV:GRAPES GFS 使用的 -3 小时
  • 1HR:GRAPES MESO 使用的 -1 小时
forecastHour := int(forecastTime.Hours())

startTime4DV := startTime.Add(time.Hour * -3)
startTime1HR := startTime.Add(time.Hour * -1)
tpVariable := TimeTemplateVariable{
		StartTime:   startTime,
		Year:        startTime.Format("2006"),
		Month:       startTime.Format("01"),
		Day:         startTime.Format("02"),
		Hour:        startTime.Format("15"),
		Forecast:    fmt.Sprintf("%03d", forecastHour),
		Year4DV:     startTime4DV.Format("2006"),
		Month4DV:    startTime4DV.Format("01"),
		Day4DV:      startTime4DV.Format("02"),
		Hour4DV:     startTime4DV.Format("15"),
		Year1HR:     startTime1HR.Format("2006"),
		Month1HR:    startTime1HR.Format("01"),
		Day1HR:      startTime1HR.Format("02"),
		Hour1HR:     startTime1HR.Format("15"),
		Forecast1HR: fmt.Sprintf("%03d", forecastHour+1),
}

使用函数

预设变量简单明了,但不利于扩展,新增时间参数需要修改源代码,无法在配置文件中直接使用。 既然上述两组预设变量生成方式几乎相同,可以创建模板函数实现动态计算。

首先创建需要的函数,包括生成新的起报时间和从时间中提取年月日时。

func generateStartTime(startTime time.Time, hour int) time.Time {
	newStartTime := startTime.Add(time.Hour * time.Duration(hour))
	return newStartTime
}

func getYear(startTime time.Time) string {
	return startTime.Format("2006")
}

func getMonth(startTime time.Time) string {
	return startTime.Format("01")
}

func getDay(startTime time.Time) string {
	return startTime.Format("02")
}

func getHour(startTime time.Time) string {
	return startTime.Format("15")
}

模板中函数的调用形式如下所示,通过管道符号 | 实现链式调用

function1 param1 param2 ... | function2 ...

4DV 系列预定义变量替换为函数调用:

注:使用 \ 实现字符串跨行合并不加空格

file_name: "postvar{generateStartTime .StartTime -3 | getYear}\
	{generateStartTime .StartTime -3 | getMonth}\
	{generateStartTime .StartTime -3 | getDay}\
	{generateStartTime .StartTime -3 | getHour}_{.Forecast}"

创建 template.FuncMap 将函数添加到模板中,并解析模板,返回文件名 fileName

fileNameTemplate := template.Must(template.New("fileName").Funcs(template.FuncMap{
	"generateStartTime": generateStartTime,
	"getYear":           getYear,
	"getMonth":          getMonth,
	"getDay":            getDay,
	"getHour":           getHour,
}).Delims("{", "}").Parse(item))
var fileNameBuilder strings.Builder
err := fileNameTemplate.Execute(&fileNameBuilder, tpVar)
// ...skip error handler...
fileName := fileNameBuilder.String()

使用函数的劣势在于模板内容明显增加,失去了预设变量的简洁性。 当前 nwpc-data-client 配置文件中仅将文件名字段 file_name 和路径字段 path 中字符串当成 GO 模板,我后续会尝试将整个 YAML 都作为 GO 模板,将计算出的起报时刻作为模板变量,进一步简化模板内容。

参考

GO text/template 官方文档

nwpc-oper/nwpc-data-client 项目:

https://github.com/nwpc-oper/nwpc-data-client