当前位置:首页 > 文章列表 > Golang > Go教程 > Go fitsio 怎样向FITS表追加一条结构化记录

Go fitsio 怎样向FITS表追加一条结构化记录

来源:17golang原创 2026-10-31 01:10:56 0浏览 收藏

我第一次把观测结果写成 FITS 表时,最容易混淆的是“写入一行”和“把表写进文件”其实是两件事。用 Go fitsio 时,可以先准备一个带 fits 标签的结构体,把它作为表模式,再调用 Table.Write 填入一条记录,最后调用 File.Write 提交这个表 HDU。

官方地址:https://pkg.go.dev/github.com/astrogo/fitsio

本文里的“追加”限定为向新建 FITS 文件中的表追加一条记录。fitsio 的公开文档把 Open 定义为只读入口,所以不要把下面的写法理解成对已有 FITS 文件的原地更新方案。

先判断你手里的是新文件还是已有文件

如果目标是生成一个新的观测文件,处理边界很清楚:os.File 负责文件句柄,fitsio.Create 创建写入模式的 FITS 对象,NewTableFrom 根据结构体建立表,Table.Write 写当前表行,File.Write 把表作为 HDU 写入文件。

如果目标是打开已有文件再改写,先停下来确认库版本和实现是否提供可用的读写路径。官方包文档中 Open 是只读打开,本文不把“追加”包装成原地修改。

用结构体先确定 FITS 表的列布局

说明文件资源、表对象、当前行数据、HDU 提交和关闭动作的静态责任边界。
图2:边界说明图,展示 Table.Write、File.Write 与资源关闭的职责,不是并发运行证据。

我更愿意先写一个专门表示一行数据的结构体,而不是直接手工拼列。这样列名和 Go 字段放在一起,后面排查“写入列顺序不对”时,定位范围会小很多。结构体标签使用 FITS 列名,字段类型则决定写入值的 Go 类型。

Go Observation 结构体字段映射到 FITS OBSERVATIONS 表列并交给 Table.Write 的静态说明图
图1:结构说明图,展示 Go 结构体到 FITS 表列的对应关系,不是运行截图。
type Observation struct {
    // fits 标签固定列名,字段类型对应要写入的值类型。
    ID int64 `fits:"ID"`
    // Flux 保存测量值,使用浮点类型避免把小数截断。
    Flux float64 `fits:"FLUX"`
    // Band 保存观测波段,列名与结构体字段可以保持同义但不必同名。
    Band string `fits:"BAND"`
}

这里的结构体值既可以作为 NewTableFrom 的模式样本,也可以作为后面 Table.Write 的一行数据。模式样本不需要填业务值;真正写入时再创建一个带值的 Observation。

用 NewTableFrom 和 Table.Write 写入一条记录

写表时我会把“表结构”和“当前行数据”分开命名。这样即使后续要连续写多行,也只需要重复构造数据值并调用 Write,不必重新创建表。

package main

import (
    "log"
    "os"

    fitsio "github.com/astrogo/fitsio"
)

type Observation struct {
    // 标签确定 FITS 列名,类型决定写入值的形态。
    ID int64 `fits:"ID"`
    Flux float64 `fits:"FLUX"`
    Band string `fits:"BAND"`
}

func writeOne(path string) error {
    // 外层文件由调用方创建和关闭,避免把底层句柄交给 fitsio 代管。
    out, err := os.Create(path)
    if err != nil {
        return err
    }
    defer out.Close()

    fits, err := fitsio.Create(out)
    if err != nil {
        return err
    }
    defer fits.Close()

    // 零值结构体只用来描述表的列布局,不代表要写入的那一行。
    table, err := fitsio.NewTableFrom("OBSERVATIONS", Observation{}, fitsio.BINARY_TBL)
    if err != nil {
        return err
    }
    defer table.Close()

    row := Observation{ID: 1001, Flux: 12.75, Band: "r"}
    // Table.Write 把这一组字段写入表的当前行。
    if err := table.Write(row); err != nil {
        return err
    }

    // File.Write 负责把已经填充的表 HDU 写入 FITS 文件。
    if err := fits.Write(table); err != nil {
        return err
    }
    return nil
}

func main() {
    if err := writeOne("observations.fits"); err != nil {
        log.Fatal(err)
    }
}

这个示例的关键不是把所有 API 都塞进一个函数,而是保留三条可检查的边界:NewTableFrom 定义表,Table.Write 写记录,File.Write 写 HDU。需要多条记录时,创建一次 table,按顺序多次调用 table.Write,最后再调用一次 fits.Write。

写入失败时,先保留可回退的输出

生产任务里我不会直接把正式文件名交给写入流程。因为结构体类型不匹配、表定义失败或底层写入失败时,目标路径可能只得到半成品。更稳妥的做法是先写临时文件,所有写入动作返回成功后再改名。

func publishTable(finalPath string, row Observation) error {
    tempPath := finalPath + ".tmp"
    // 临时路径失败时只影响待提交文件,不覆盖上一次正式结果。
    out, err := os.Create(tempPath)
    if err != nil {
        return err
    }

    fits, err := fitsio.Create(out)
    if err != nil {
        out.Close()
        os.Remove(tempPath)
        return err
    }
    table, err := fitsio.NewTableFrom("OBSERVATIONS", Observation{}, fitsio.BINARY_TBL)
    if err != nil {
        fits.Close()
        out.Close()
        os.Remove(tempPath)
        return err
    }

    // 先写行,再提交 HDU;任何一步失败都不替换正式文件。
    err = table.Write(row)
    if err == nil {
        err = fits.Write(table)
    }
    table.Close()
    closeErr := fits.Close()
    fileErr := out.Close()
    if err == nil {
        err = closeErr
    }
    if err == nil {
        err = fileErr
    }
    if err != nil {
        os.Remove(tempPath)
        return err
    }

    // 只有临时文件完整关闭后才替换正式文件。
    return os.Rename(tempPath, finalPath)
}

回退动作不是“重新调用一次 Write”。失败后应关闭已打开的资源、删除临时文件,并保留原正式文件;确认错误原因已修复后,再以新的临时文件重新生成。

用一张责任表确认是否真的写成功

对象负责什么常见误区
Observation{}提供列结构样本把零值模式误认为业务记录
Table.Write(row)把一行字段写入表以为它已经把整个 HDU 提交到文件
File.Write(table)写入表 HDU漏掉这一步就只是在内存中准备表对象
Table.Close / File.Close释放 fitsio 对象资源以为 File.Close 会自动关闭底层 os.File

官方文档特别说明,File.Close 不会关闭底层的 io.Reader 或 io.Writer。所以示例里仍然由 defer out.Close() 或显式关闭 os.File。遇到“文件句柄还占用”或输出无法重命名时,优先检查这条边界。

这套写法适合什么场景

如果你的任务是从 Go 服务或批处理程序生成一个新的 FITS 表,这种结构体驱动的写法足够直接:列布局集中、写行入口明确、失败时可以用临时文件回退。若你的任务是修改已有 FITS 文件、追加到已有 HDU,或者需要 FITS 原地更新,则应先确认当前库版本是否提供对应的读写能力,再决定是否换用专门的 FITS 工具链。

我自己的判断是:当记录结构稳定、输出文件由当前任务负责生成时,NewTableFrom + Table.Write + File.Write 很顺手;当文件生命周期复杂、需要原地增量更新时,不要只因为 API 名字里有 Write 就假定它支持数据库式追加。

相关问题

Table.Write 可以连续调用吗?

可以把同一个表对象作为目标,按记录逐次调用 Table.Write。表的创建和 HDU 提交不要在每一行之间重复进行。

为什么写了 Table.Write 仍然没有输出文件内容?

Table.Write 面向表的当前行;还需要把表对象交给 File.Write,并正常关闭 FITS 对象和底层文件。

fitsio.Create 会关闭 os.File 吗?

不要依赖这种假设。底层 os.File 仍应由创建它的代码负责关闭,临时文件提交流程也应在关闭成功后再改名。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go fitsio 怎样读取图像HDU的像素数组Go fitsio 怎样读取图像HDU的像素数组
上一篇
Go fitsio 怎样读取图像HDU的像素数组
Redis OBJECT FREQ 怎样读取LFU访问频率计数
下一篇
Redis OBJECT FREQ 怎样读取LFU访问频率计数
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    800次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    792次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    879次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    849次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    680次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码