发现C#没有一个很好用的库来处理nc文件,找资料花了不少时间,记录一下使用方法。两种方式,前者更纯净一些,但也太底层了,使用很麻烦; 后者封装的比较好,代码简洁,但生成出来的文件有默认生成的内容(自己还在摸索中)。
前提
都需要nuget安装SDSLite库,然后再把附件中的几个dll文件与程序放一起。
参考:
参考文档 Introduction to Scientific DataSet
方法1
1.1 写文件
public static void write_nc_test()
{
//创建文件
string filename = "newFile.nc";
int ncid, status;
status = NetCDF.nc_create(filename, CreateMode.NC_WRITE, out ncid);
//定义维度(dimensions)
int dim1_id, dim2_id;
int dim1_len = 5;
int dim2_len = 6;
IntPtr dim1_len_intp = new IntPtr(dim1_len);
IntPtr dim2_len_intp = new IntPtr(dim2_len);
status = NetCDF.nc_def_dim(ncid, "dim1", dim1_len_intp, out dim1_id);
status = NetCDF.nc_def_dim(ncid, "dim2", dim2_len_intp, out dim2_id);
//定义变量
int varid;
int[] dimids = new int[] { dim1_id, dim2_id };
status = NetCDF.nc_def_var(ncid, "Q", NcType.NC_FLOAT, dimids, out varid);
//关闭定义模式
status = NetCDF.nc_enddef(ncid);
//向变量中存入数据
float[] data = new float[dim1_len * dim2_len];
for (int i = 0; i < data.Length; i++)
{
data[i] = (float)((i + 1) * 1.0);
}
System.IntPtr start_intp = new IntPtr(0);
System.IntPtr[] start = new IntPtr[] { start_intp, start_intp };
System.IntPtr[] count = new IntPtr[] { dim1_len_intp, dim2_len_intp };
status = NetCDF.nc_put_vara_float(ncid, varid, start, count, data);
//关闭文件
status = NetCDF.nc_close(ncid);
}
1.2 读文件
注意:原始读取出来的数是一维的,还需要二次处理
public static void read_nc_test()
{
//打开文件
string filename = "newFile.nc"; //"etp.nc";
int ncid, status;
status = NetCDFInterop.NetCDF.nc_open(filename, CreateMode.NC_NOWRITE, out ncid);
if (status != 0) { Console.WriteLine("无法打开文件"); return; }
//文件里有几个变量
int nvars;
int[] varids = new int[6];
status = NetCDFInterop.NetCDF.nc_inq_varids(ncid, out nvars, varids);
//各变量的信息
string varName = "";
NcType nctype;
int ndims, natts;
int[] nd = new int[4];
for (int i = 0; i < nvars; i++)
{
status = NetCDFInterop.NetCDF.nc_inq_var(ncid, varids[0], out varName, out nctype, out ndims, nd, out natts);
Console.WriteLine("变量{0}是:{1}", i + 1, varName);
}
// 查询数据长度
int dim1_id, dim2_id;
IntPtr dim1_len_ptr, dim2_len_ptr;
NetCDFInterop.NetCDF.nc_inq_dimid(ncid, "dim1", out dim1_id);
NetCDFInterop.NetCDF.nc_inq_dimlen(ncid, dim1_id, out dim1_len_ptr);
NetCDFInterop.NetCDF.nc_inq_dimid(ncid, "dim2", out dim2_id);
NetCDFInterop.NetCDF.nc_inq_dimlen(ncid, dim2_id, out dim2_len_ptr);
//查询数据 (只能查询出一维数组,还需要二次处理)
System.IntPtr start_intp = new IntPtr(0);
System.IntPtr[] start = new IntPtr[] { start_intp, start_intp };
System.IntPtr[] count = new IntPtr[] { dim1_len_ptr, dim2_len_ptr };
float[] data = new float[dim1_len_ptr.ToInt32() * dim2_len_ptr.ToInt32()];
status = NetCDFInterop.NetCDF.nc_get_vara_float(ncid, varids[0], start, count, data);
// 对一维数组的处理
//关闭文件
NetCDFInterop.NetCDF.nc_close(ncid);
}
方法2
using Microsoft.Research.Science.Data;
using Microsoft.Research.Science.Data.Imperative;
2.1 写文件
public static void write_nc(string nc_file)
{
using (DataSet ds = DataSet.Open($"msds:nc?file={nc_file}&openMode=create"))
{
double[] data = new double[10];
ds.AddVariable<double>("DATA", data, "x");
//ds.Add<double[]>("Data","x"); //或者可以用这两行
//ds.PutData<double[]>("Data", data);
}
}
2.2 读文件
语法非常简洁!
public static void read_nc(string nc_file)
{
using (DataSet ds = DataSet.Open($"msds:nc?file={nc_file}&openMode=readOnly"))
{
var data = ds.GetData<float[,]>("Data");
//Console.WriteLine(ds);
//Console.WriteLine("name:" + ds.Variables[0].Name);
//Console.WriteLine(ds.Metadata["comment"]);
}
}
踩坑记录
如果显示如下错误,把 msvcr100.dll 也放到程序目录里,或者安装Visual C++ Redistributable。
Unhandled exception. Microsoft.Research.Science.Data.DataSetCreateException: Failed to create DataSet instance from uri msds:nc?file=par.nc&openMode=readOnly: Unable to load DLL ‘netcdf’ or one of its dependencies: 找不到指定的模块。 (0x8007007E)
—> System.DllNotFoundException: Unable to load DLL ‘netcdf’ or one of its dependencies: 找不到指定的模块。 (0x8007007E)
写入全局属性
// 全局属性 (要在nc_enddef之前;varid是-1)
status = NetCDF.nc_put_att_text(ncid, -1, "Date", "2021/1/1");
if (status != 0)
throw new Exception("写入属性失败: " + NetCDF.nc_strerror(status));
951

被折叠的 条评论
为什么被折叠?



