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 模板,将计算出的起报时刻作为模板变量,进一步简化模板内容。
参考
nwpc-oper/nwpc-data-client 项目: