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

如何在C语言中按标记拆分OpenGL着色器文件字符串?

解决方案

要实现拆分带标记的着色器文件,你可以先修正原文件读取函数的字符串终止符问题,再通过字符串查找或逐行读取的方式拆分内容。以下是两种可行的实现方式:

方法一:一次性读取文件后拆分(适合小文件)

首先修正你的FileToString函数,确保读取的缓冲区是有效的C字符串(添加终止符):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 用于存储拆分后的着色器源码
typedef struct {
    char* vertex_shader;
    char* fragment_shader;
} ShaderSources;

static char* FileToString(const char* filePath)
{
    FILE* source = fopen(filePath, "r");
    if(source == NULL)
    {
        printf("Failed to open %s!\n", filePath);
        return NULL;
    }

    fseek(source, 0, SEEK_END);
    long length = ftell(source);
    fseek(source, 0, SEEK_SET);

    // 多分配1字节存储字符串终止符
    char* buffer = (char*)malloc(length + 1);
    if(buffer)
    {
        fread(buffer, 1, length, source);
        buffer[length] = '\0'; // 添加终止符,确保是合法C字符串
    }

    fclose(source);
    return buffer;
}

然后实现拆分函数,通过查找标记位置分割内容:

ShaderSources SplitShaderSources(const char* source)
{
    ShaderSources sources = {NULL, NULL};
    if(!source) return sources;

    const char* vertex_tag = "#shader vertex";
    const char* fragment_tag = "#shader fragment";

    // 定位顶点着色器起始位置(跳过标记行)
    const char* vertex_start = strstr(source, vertex_tag);
    if(!vertex_start) return sources;

    vertex_start += strlen(vertex_tag);
    // 跳过标记后的换行符(兼容\n或\r\n)
    while(*vertex_start != '\0' && (*vertex_start == '\n' || *vertex_start == '\r'))
        vertex_start++;

    // 定位片段着色器起始位置,作为顶点着色器的结束
    const char* vertex_end = strstr(vertex_start, fragment_tag);
    if(!vertex_end) return sources;

    // 分配内存并复制顶点着色器内容
    size_t vertex_len = vertex_end - vertex_start;
    sources.vertex_shader = (char*)malloc(vertex_len + 1);
    if(sources.vertex_shader)
    {
        strncpy(sources.vertex_shader, vertex_start, vertex_len);
        sources.vertex_shader[vertex_len] = '\0';
    }

    // 处理片段着色器
    const char* fragment_start = vertex_end + strlen(fragment_tag);
    while(*fragment_start != '\0' && (*fragment_start == '\n' || *fragment_start == '\r'))
        fragment_start++;

    size_t fragment_len = strlen(fragment_start);
    sources.fragment_shader = (char*)malloc(fragment_len + 1);
    if(sources.fragment_shader)
    {
        strcpy(sources.fragment_shader, fragment_start);
    }

    return sources;
}

使用示例

int main()
{
    char* file_content = FileToString("shaders.shader");
    if(!file_content) return 1;

    ShaderSources sources = SplitShaderSources(file_content);

    // 使用着色器内容
    if(sources.vertex_shader)
    {
        printf("Vertex Shader:\n%s\n", sources.vertex_shader);
        free(sources.vertex_shader);
    }
    if(sources.fragment_shader)
    {
        printf("Fragment Shader:\n%s\n", sources.fragment_shader);
        free(sources.fragment_shader);
    }

    free(file_content);
    return 0;
}

方法二:逐行读取并拆分(适合大文件)

如果着色器文件较大,逐行读取可以减少内存占用:

ShaderSources LoadAndSplitShader(const char* filePath)
{
    ShaderSources sources = {NULL, NULL};
    FILE* file = fopen(filePath, "r");
    if(!file)
    {
        printf("Failed to open %s!\n", filePath);
        return sources;
    }

    char line[1024];
    enum { NONE, VERTEX, FRAGMENT } current_mode = NONE;

    char* vertex_buf = NULL;
    size_t vertex_buf_size = 0;
    char* fragment_buf = NULL;
    size_t fragment_buf_size = 0;

    while(fgets(line, sizeof(line), file))
    {
        // 检测标记行,切换当前模式
        if(strstr(line, "#shader vertex"))
        {
            current_mode = VERTEX;
            continue;
        }
        if(strstr(line, "#shader fragment"))
        {
            current_mode = FRAGMENT;
            continue;
        }

        // 根据当前模式追加行到对应缓冲区
        if(current_mode == VERTEX)
        {
            size_t line_len = strlen(line);
            vertex_buf = realloc(vertex_buf, vertex_buf_size + line_len + 1);
            if(vertex_buf)
            {
                strcpy(vertex_buf + vertex_buf_size, line);
                vertex_buf_size += line_len;
            }
        }
        else if(current_mode == FRAGMENT)
        {
            size_t line_len = strlen(line);
            fragment_buf = realloc(fragment_buf, fragment_buf_size + line_len + 1);
            if(fragment_buf)
            {
                strcpy(fragment_buf + fragment_buf_size, line);
                fragment_buf_size += line_len;
            }
        }
    }

    sources.vertex_shader = vertex_buf;
    sources.fragment_shader = fragment_buf;

    fclose(file);
    return sources;
}

注意事项

  • 所有通过malloc/realloc分配的内存,使用完成后必须调用free释放,避免内存泄漏。
  • 两种方法都兼容\n和\r\n换行格式。
  • 方法一代码更简洁,但会一次性加载整个文件到内存;方法二更适合大文件,内存使用更高效。

内容的提问来源于stack exchange,提问作者Dr-Galunga

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 18:15:36