You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用C语言写入带AppendedData的.vtu文件时Paraview报错求助

问题:Paraview读取VTU文件时提示Cells的offsets数组错误

我尝试用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标签有关,但不确定具体原因,希望有人能帮忙找出问题所在。


问题分析与解决

核心错误点

  1. 连接性数组越界
    代码中connectivity数组包含值4,但<Piece>标签中NumberOfPoints="4",点索引范围是0-3,4属于非法索引,会导致Paraview解析数据时出现异常,进而误判offsets数组的有效性。

  2. Header类型与Size变量不匹配
    VTK文件头中指定header_type="UInt64",意味着AppendedData中每个数据块前的长度标识是8字节的无符号64位整数,但代码中用uint32_t存储size_points等变量,且写入时按4字节处理,导致Paraview读取数据时偏移计算完全错误,把非offsets的数据当成offsets解析,触发报错。

  3. 偏移计算错误
    由于header_type是UInt64,每个数据块前的size占8字节,而非4字节,代码中计算offset_connectivity等偏移时使用4 + size_points是错误的,应该用8 + size_points,后续偏移也要对应调整。

  4. 字节交换逻辑错误
    针对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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 21:22:03