使用C语言写入带AppendedData的.vtu文件时Paraview报错求助
我尝试用C语言写入包含原始二进制数据(存储在AppendedData段)的.vtu文件,但在Paraview中打开输出文件时出现如下错误:
"无法读取块0中Cells的单元连接性,因为offsets数组不是单调递增或起始值不为0"
以下是我的示例代码:
#include <byteswap.h> int is_big_endian(void) { union { uint32_t i; char c[4]; } e = {0x01000000}; return e.c[0] == 1; } void write_vtu_with_appended_data(const char* filename) { FILE* file = fopen(filename, "wb"); // Use binary mode for writing if (file == NULL) { printf("Failed to open the file.\n"); return; } // Before writing to the file, check the system's endianness and convert if necessary double points[] = {0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0}; int connectivity[] = {0, 1, 2, 3, 1, 2, 3, 4}; int offsets[] = {4, 8}; // corrected offsets unsigned char types[] = {9, 9}; // corrected types // Size of data blocks uint32_t size_points = sizeof(points); uint32_t size_connectivity = sizeof(connectivity); uint32_t size_offsets = sizeof(offsets); uint32_t size_types = sizeof(types); if (is_big_endian()) { // Byte swap the data if necessary for (int i = 0; i < 12; ++i) points[i] = __bswap_64(points[i]); for (int i = 0; i < 8; ++i) connectivity[i] = __bswap_32(connectivity[i]); for (int i = 0; i < 2; ++i) // Update the loop limit offsets[i] = __bswap_32(offsets[i]); // no need to swap 'types' because they are 1 byte // Swap sizes as well size_points = __bswap_32(size_points); size_connectivity = __bswap_32(size_connectivity); size_offsets = __bswap_32(size_offsets); size_types = __bswap_32(size_types); } // Write the file header fprintf(file, "<?xml version=\"1.0\"?>\n"); fprintf(file, "<VTKFile type=\"UnstructuredGrid\" version=\"1.0\" byte_order=\"LittleEndian\" header_type=\"UInt64\">\n"); fprintf(file, "<UnstructuredGrid>\n"); fprintf(file, "<Piece NumberOfPoints=\"4\" NumberOfCells=\"2\">\n"); // Write the point data fprintf(file, "<Points>\n"); fprintf(file, "<DataArray Name=\"points\" NumberOfComponents=\"3\" type=\"Float64\" format=\"appended\" offset=\"0\"/>\n"); fprintf(file, "</Points>\n"); // Compute offsets for the appended data uint64_t offset_connectivity = 4 + size_points; uint64_t offset_offsets = offset_connectivity + 4 + size_connectivity; uint64_t offset_types = offset_offsets + 4 + size_offsets; // Write the connectivity and offsets fprintf(file, "<Cells>\n"); fprintf(file, "<DataArray Name=\"connectivity\" NumberOfComponents=\"1\" type=\"Int32\" format=\"appended\" offset=\"%llu\"/>\n", offset_connectivity); fprintf(file, "<DataArray Name=\"offsets\" NumberOfComponents=\"1\" type=\"Int32\" format=\"appended\" offset=\"%llu\"/>\n", offset_offsets); fprintf(file, "<DataArray Name=\"types\" NumberOfComponents=\"1\" type=\"UInt8\" format=\"appended\" offset=\"%llu\"/>\n", offset_types); fprintf(file, "</Cells>\n"); fprintf(file, "</Piece>\n"); fprintf(file, "</UnstructuredGrid>\n"); // Write the appended data section // '_' character is used as a separator as per the VTK file format specification fprintf(file, "<AppendedData encoding=\"raw\">\n_"); // Write point data (binary format) fwrite(&size_points, sizeof(uint32_t), 1, file); fwrite(points, sizeof(double), 12, file); // Write connectivity (binary format) fwrite(&size_connectivity, sizeof(uint32_t), 1, file); fwrite(connectivity, sizeof(int), 8, file); // Write offsets (binary format) fwrite(&size_offsets, sizeof(uint32_t), 1, file); fwrite(offsets, sizeof(int), 2, file); // Update the number of offsets // Write types (binary format) fwrite(&size_types, sizeof(uint32_t), 1, file); fwrite(types, sizeof(unsigned char), 2, file); // Update the number of types // Write the closing tags fprintf(file, "\n</AppendedData>\n"); fprintf(file, "</VTKFile>\n"); if (fclose(file) != 0) { printf("Failed to close the file.\n"); } else { printf("VTU file written successfully!\n"); } }
我怀疑问题与offset标签有关,但不确定具体原因,希望有人能帮忙找出问题所在。
核心错误点
连接性数组越界
代码中connectivity数组包含值4,但<Piece>标签中NumberOfPoints="4",点索引范围是0-3,4属于非法索引,会导致Paraview解析数据时出现异常,进而误判offsets数组的有效性。Header类型与Size变量不匹配
VTK文件头中指定header_type="UInt64",意味着AppendedData中每个数据块前的长度标识是8字节的无符号64位整数,但代码中用uint32_t存储size_points等变量,且写入时按4字节处理,导致Paraview读取数据时偏移计算完全错误,把非offsets的数据当成offsets解析,触发报错。偏移计算错误
由于header_type是UInt64,每个数据块前的size占8字节,而非4字节,代码中计算offset_connectivity等偏移时使用4 + size_points是错误的,应该用8 + size_points,后续偏移也要对应调整。字节交换逻辑错误
针对size变量的字节交换应该用__bswap_64而非__bswap_32,因为size是UInt64类型。
修正后的代码
#include <byteswap.h> #include <stdint.h> #include <stdio.h> int is_big_endian(void) { union { uint32_t i; char c[4]; } e = {0x01000000}; return e.c[0] == 1; } void write_vtu_with_appended_data(const char* filename) { FILE* file = fopen(filename, "wb"); if (file == NULL) { printf("Failed to open the file.\n"); return; } // 修正连接性数组:移除非法索引4,替换为合法的0 double points[] = {0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0}; int connectivity[] = {0, 1, 2, 3, 1, 2, 3, 0}; int offsets[] = {4, 8}; unsigned char types[] = {9, 9}; // 使用uint64_t匹配header_type="UInt64" uint64_t size_points = sizeof(points); uint64_t size_connectivity = sizeof(connectivity); uint64_t size_offsets = sizeof(offsets); uint64_t size_types = sizeof(types); if (is_big_endian()) { for (int i = 0; i < 12; ++i) points[i] = __bswap_64(points[i]); for (int i = 0; i < 8; ++i) connectivity[i] = __bswap_32(connectivity[i]); for (int i = 0; i < 2; ++i) offsets[i] = __bswap_32(offsets[i]); // 对UInt64类型的size进行字节交换 size_points = __bswap_64(size_points); size_connectivity = __bswap_64(size_connectivity); size_offsets = __bswap_64(size_offsets); size_types = __bswap_64(size_types); } fprintf(file, "<?xml version=\"1.0\"?>\n"); fprintf(file, "<VTKFile type=\"UnstructuredGrid\" version=\"1.0\" byte_order=\"LittleEndian\" header_type=\"UInt64\">\n"); fprintf(file, "<UnstructuredGrid>\n"); fprintf(file, "<Piece NumberOfPoints=\"4\" NumberOfCells=\"2\">\n"); fprintf(file, "<Points>\n"); fprintf(file, "<DataArray Name=\"points\" NumberOfComponents=\"3\" type=\"Float64\" format=\"appended\" offset=\"0\"/>\n"); fprintf(file, "</Points>\n"); // 修正偏移计算:每个size占8字节 uint64_t offset_connectivity = 8 + size_points; uint64_t offset_offsets = offset_connectivity + 8 + size_connectivity; uint64_t offset_types = offset_offsets + 8 + size_offsets; fprintf(file, "<Cells>\n"); fprintf(file, "<DataArray Name=\"connectivity\" NumberOfComponents=\"1\" type=\"Int32\" format=\"appended\" offset=\"%llu\"/>\n", offset_connectivity); fprintf(file, "<DataArray Name=\"offsets\" NumberOfComponents=\"1\" type=\"Int32\" format=\"appended\" offset=\"%llu\"/>\n", offset_offsets); fprintf(file, "<DataArray Name=\"types\" NumberOfComponents=\"1\" type=\"UInt8\" format=\"appended\" offset=\"%llu\"/>\n", offset_types); fprintf(file, "</Cells>\n"); fprintf(file, "</Piece>\n"); fprintf(file, "</UnstructuredGrid>\n"); fprintf(file, "<AppendedData encoding=\"raw\">\n_"); // 按UInt64写入size fwrite(&size_points, sizeof(uint64_t), 1, file); fwrite(points, sizeof(double), 12, file); fwrite(&size_connectivity, sizeof(uint64_t), 1, file); fwrite(connectivity, sizeof(int), 8, file); fwrite(&size_offsets, sizeof(uint64_t), 1, file); fwrite(offsets, sizeof(int), 2, file); fwrite(&size_types, sizeof(uint64_t), 1, file); fwrite(types, sizeof(unsigned char), 2, file); fprintf(file, "\n</AppendedData>\n"); fprintf(file, "</VTKFile>\n"); if (fclose(file) != 0) { printf("Failed to close the file.\n"); } else { printf("VTU file written successfully!\n"); } }
验证说明
修正后,Paraview将能正确解析:
- 连接性数组的索引都在合法范围内
- 数据块的偏移计算准确,能正确读取offsets数组
- 字节序处理符合LittleEndian的指定要求
内容的提问来源于stack exchange,提问作者Elisan Magalhaes

