如何在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
相关产品推荐
相关产品推荐

