Просмотрите текстовый файл, заменив все экземпляры переменной ее определением / содержимым - PullRequest
0 голосов
/ 26 сентября 2018

Я попал в ментальный блок проекта, который я делаю.Я хочу просмотреть файл и заменить все экземпляры переменных их содержимым или значением.У меня есть структура данных, которая содержит переменные, и у меня есть текстовый файл, назначенный указателю файла.

Структура данных, содержащая переменные, состоит из ключа (имя переменной) и данных (определение переменной):

// Preceeds the creation of a map to store the variable names and values
struct VarMap {
    char data[1000];
    char key[1000];
};

Это определение находится в заголовочном файле для моего проекта.Ниже приведен пример текстовых файлов, с которыми я имею дело:

# A Makefile to build our 'calcmarks' project

C99     =  cc -std=c99
CFLAGS  =  -Wall -pedantic -Werror


calcmarks : calcmarks.o globals.o readmarks.o correlation.o
       $(C99) $(CFLAGS) -o calcmarks \
                  calcmarks.o globals.o readmarks.o correlation.o -lm


calcmarks.o : calcmarks.c calcmarks.h
       $(C99) $(CFLAGS) -c calcmarks.c

globals.o : globals.c calcmarks.h
       $(C99) $(CFLAGS) -c globals.c

readmarks.o : readmarks.c calcmarks.h
       $(C99) $(CFLAGS) -c readmarks.c

correlation.o : correlation.c calcmarks.h
       $(C99) $(CFLAGS) -c correlation.c

Как вы, возможно, уже знаете из приведенного выше кода, я пишу программу, которая может реализовать небольшое подмножество программы сделать .Переменным в приведенном выше текстовом файле предшествует знак доллара «$», а имя переменной помещается в скобках «()», как в $ (C99) или $ (CFLAGS) в приведенном выше примере;хотя могут существовать и другие имена переменных.

Я уже написал часть программы для обработки этих переменных, последняя часть этой функции еще не завершена, но я надеюсь использовать ее для выполнения фактической "замены переменных":

#include "globals.h"

#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdarg.h>

// Store and replace all variables used in the makefile
void processVariables(FILE* spData) {

    // Initialise the counting variable buffer to hold the file line by line
    varCount = 0;
    char buffer[10000];
    while (fgets(buffer , sizeof(buffer) , spData) != NULL) {
        // Skip commented lines (preceede by hash '#')
        if (buffer[0] == '#') continue;
        for (int i = 0; buffer[i] != '\n' ; i++) {
            if (buffer[i] == '=') {
                // Increment if line with equals sign is found
                varCount++;
                break;
            }
        }
    }

    // Debugging print statement
    printf ("varCount has counted %d equals signs.\n\n" , varCount);

    // This will hold the variables
    struct VarMap variables[varCount + 4];
    int j = 0;
    rewind(spData);
    // Parse the makefile/bakefile (what is it even now) and store the variable names and assignments
    while (fgets(buffer , sizeof(buffer) , spData) != NULL) {
        if (buffer[0] == '#') continue;
        char* p = strstr(buffer,"=");
        if (p) {
            *p++ = 0;
            // If copy size is too small, change the final number in the function
            strncpy(variables[j].key, buffer,1000);
            strncpy(variables[j].data, p,1000);
            j++;
        }
    }

    // Appending the general variables to the variable array
    strcpy(variables[varCount].key , "PID");
    strcpy(variables[varCount].data , "getpid()");
    strcpy(variables[varCount + 1].key , "PPID");
    strcpy(variables[varCount + 1].data , "getppid()");
    strcpy(variables[varCount + 2].key , "PWD");
    strcpy(variables[varCount + 2].data , "getcwd()");
    strcpy(variables[varCount + 3].key , "RAND");
    strcpy(variables[varCount + 3].data , "rand()");

    // Debugging print statement (All code above this point is correct)
    printf("List of all variables as they stand here:\n");
    for(int i = 0; i < varCount + 4; i++) {
        printf("Key: %s, Value: %s\n" , variables[i].key , variables[i].data);
    }

    // Replacing the variables
    // Go through the file and re-write it line by line, keep calling rewind(spData);
    // and parse through the file until there are no more '$' signs
    rewind(spData);
    char copyStream[10000];
    bool noDollarSigns = false;
    // While there are still dollar signs in the file, indicating more variable substitution needs to occur
    while (noDollarSigns == false) {
        while (fgets(copyStream , sizeof(copyStream) , spData) != NULL) {

        }
    }


}

Как видите, последние 2 вложенных 'while's - это пространство, в котором я собирался выполнить фактическую подстановку переменных.Основные переменные, которые вы видите, - это переменные, которые программа будет подставлять в каждый обрабатываемый make-файл.

И функция main для контекста:

int main(int argc, const char * argv[]) {

    char filepath[1000];
    printf("Enter the filepath of the Bakefile or bakefile: ");
    scanf("%s" , filepath);
    FILE* spData;
    spData = fopen(filepath , "r");
    if (spData == NULL) {
        printf ("Cannot open file.");
        exit(EXIT_FAILURE);
    }

    processVariables(spData);

    fclose(spData);
    return 0;
}

Вызов mainФункция с функцией processVariables в том виде, в котором она написана, и все заголовочные файлы в актуальном состоянии, с указанным выше make-файлом, дает следующий вывод:

Enter the filepath of the Bakefile or bakefile: /Users/Admin/Documents/Makefiles/Test1.txt
varCount has counted 2 equals signs.

List of all variables as they stand here:
Key: C99     , Value:   cc -std=c99

Key: CFLAGS  , Value:   -Wall -pedantic -Werror

Key: PID, Value: getpid()
Key: PPID, Value: getppid()
Key: PWD, Value: getcwd()
Key: RAND, Value: rand()

Первые 2 переменные относятся к рассматриваемому make-файлу, аостальные 4 являются общими и должны быть заменены на все make-файлы.

Я уже задавал здесь много вопросов о помощи по моему проекту, и все ответы до сих пор были чрезвычайно полезны - большое спасибо!

Итак, в основном:

Как мне написать код для прохождения через текстовый файл и заменить все экземпляры переменных их определениями, данными в моей структуре данных?Если такой код не должен быть записан в функции processVariables, куда этот код должен идти в моей программе?

РЕДАКТИРОВАТЬ 1: @fotang предоставил полу-полный сегмент кода,который я вставил и немного доработал, чтобы исправить тут и там.Вот новая функция processVariables с его вставленным кодом:

void processVariables(FILE* spData) {

    // Initialise the counting variable buffer to hold the file line by line
    varCount = 0;
    char buffer[10000];
    while (fgets(buffer , sizeof(buffer) , spData) != NULL) {
        // Skip commented lines (preceede by hash '#')
        if (buffer[0] == '#') continue;
        for (int i = 0; buffer[i] != '\n' ; i++) {
            if (buffer[i] == '=') {
                // Increment if line with equals sign is found
                varCount++;
                break;
            }
        }
    }

    // Debugging print statement
    printf ("varCount has counted %d equals signs.\n\n" , varCount);

    // This will hold the variables
    struct VarMap variables[varCount + 4];
    int j = 0;
    rewind(spData);
    // Parse the makefile/bakefile (what is it even now bruh) and store the variable names and assignments
    while (fgets(buffer , sizeof(buffer) , spData) != NULL) {
        if (buffer[0] == '#') continue;
        char* p = strstr(buffer , "=");
        if (p) {
            *p++ = 0;
            // If copy size is too small, change the final number in the function
            strncpy(variables[j].key, buffer,1000);
            strncpy(variables[j].data, p,1000);

            // Get rid of any trailing newline characters in the data
            char* newline;
            if ((newline = strchr(variables[j].data, '\n')) != NULL)
                *newline = '\0';

            j++;
        }
    }

    // Appending the general variables to the variable array
    strcpy(variables[varCount].key , "PID");
    strcpy(variables[varCount].data , "getpid()");
    strcpy(variables[varCount + 1].key , "PPID");
    strcpy(variables[varCount + 1].data , "getppid()");
    strcpy(variables[varCount + 2].key , "PWD");
    strcpy(variables[varCount + 2].data , "getcwd()");
    strcpy(variables[varCount + 3].key , "RAND");
    strcpy(variables[varCount + 3].data , "rand()");

    // Debugging print statement
    printf("List of all variables as they stand here:\n");
    for(int i = 0; i < varCount + 4; i++) {
        printf("Key: %s, Value: %s\n" , variables[i].key , variables[i].data);
    }

    // Replacing the variables
    // Go through the file and re-write it line by line
    // parse through the file until there are no more '$' signs
    FILE* outputPtr = fopen("/Users/Admin/Documents/Makefiles/Output.txt","w");
    rewind(spData);
    while (fgets(buffer , sizeof(buffer) , spData) != NULL) {
        // Comment lines need no variable substitution so we print them as written
        if (buffer[0] == '#'){
            fputs(buffer,outputPtr);
            continue;
        }
        // Copying the buffer character by character until a dollar '$' sign is reached
        char* p = buffer;
        while (*p) {
            if (*p != '$') {
                fputc(*p++ , outputPtr);
                continue;
            }
            p++;

            if(*p != '('){
                fputc(*p++ , outputPtr);
                continue;
            }
            // Get the variable name (key)
            char *s = ++p;
            char key[1000];
            while(*s != ')') s++;
            strncpy(key, p, s - p);
            key[s - p] = 0;
            p = s + 1;
            // Fetch the contents of the variable from the structure and substitute
            for(int i = 0; i < varCount + 4; i++)
                if(strcmp(variables[i].key, key)){
                    fprintf(outputPtr, "%s", variables[i].data);
                    break;
                }
        }
    }
}

Однако, если эта функция теперь вызывается на ткани со следующим текстом: (имя файла - Text1.txt)

# A Makefile to build our 'calcmarks' project

# Variable Declarations
C99     =  cc -std=c99
CFLAGS  =  -Wall -pedantic -Werror


# Command lines
calcmarks : calcmarks.o globals.o readmarks.o correlation.o
       $(C99) $(CFLAGS) -o calcmarks \
                  calcmarks.o globals.o readmarks.o correlation.o -lm


calcmarks.o : calcmarks.c calcmarks.h
       $(C99) $(CFLAGS) -c calcmarks.c

globals.o : globals.c calcmarks.h
       $(C99) $(CFLAGS) -c globals.c

readmarks.o : readmarks.c calcmarks.h
       $(C99) $(CFLAGS) -c readmarks.c

correlation.o : correlation.c calcmarks.h
       $(C99) $(CFLAGS) -c correlation.c

varTest:
       $(PID) $(PPID) $(PWD) $(RAND)

Это следующее содержимое выходного файла:

# A Makefile to build our 'calcmarks' project

# Variable Declarations
C99     =  cc -std=c99
CFLAGS  =  -Wall -pedantic -Werror


# Command lines
calcmarks : calcmarks.o globals.o readmarks.o correlation.o
         cc -std=c99   cc -std=c99 -o calcmarks \
                  calcmarks.o globals.o readmarks.o correlation.o -lm


calcmarks.o : calcmarks.c calcmarks.h
         cc -std=c99   cc -std=c99 -c calcmarks.c

globals.o : globals.c calcmarks.h
         cc -std=c99   cc -std=c99 -c globals.c

readmarks.o : readmarks.c calcmarks.h
         cc -std=c99   cc -std=c99 -c readmarks.c

correlation.o : correlation.c calcmarks.h
         cc -std=c99   cc -std=c99 -c correlation.c

varTest:
         cc -std=c99   cc -std=c99   cc -std=c99   cc -std=c99

Как видите, каждая переменная была заменена только содержимым первой переменной в структуре variable.

РЕДАКТИРОВАТЬ 2: Замена strcmp() на !strcmp() в конечном , если блок производит следующий вывод:

# A Makefile to build our 'calcmarks' project

# Variable Declarations
C99     =  cc -std=c99
CFLAGS  =  -Wall -pedantic -Werror


# Command lines
calcmarks : calcmarks.o globals.o readmarks.o correlation.o
                         calcmarks.o globals.o readmarks.o correlation.o -lm


calcmarks.o : calcmarks.c calcmarks.h

globals.o : globals.c calcmarks.h

readmarks.o : readmarks.c calcmarks.h

correlation.o : correlation.c calcmarks.h

varTest:
       getpid() getppid() getcwd() rand()

Как видите, на этот раз все переменные удаляются без подстановки, за исключением 4 общих добавленных переменных.

1 Ответ

0 голосов
/ 26 сентября 2018

Подход был упомянут в разделе комментариев.Откройте новый файл, снова просканируйте «Makefile» и подставьте переменные, записав замещенный вывод в новый файл.Пусть выходной файл будет называться /tmp/output.Вот некоторый код (непроверенный):

Обновление : @DanielPryden поднял две проблемы в разделе комментариев относительно опасностей простого копирования и вставки этого ответа.Я пытаюсь решить эту проблему внутри цикла, который начинается с "char * p = buffer; while (* p) {".Конечно, любой, кто делает что-то серьезное, будет использовать лексический анализатор, такой как flex, и хранить ключи в дереве AVL или что-то подобное.

 FILE *output=fopen("/tmp/output","w");
rewind(spData);
while (fgets(buffer , sizeof(buffer) , spData) != NULL) {
    if (buffer[0] == '#'){
        fputs(buffer,output);
        continue;
    }
    char *p=buffer;
    while(*p){
        if(*p!='$'){
            fputc(output, *p++);
            continue;
        }
        p++;
        if(*p!='('){
            fputc(output, *(p-1)); // bring back '$'
            fputc(output, *p++);
            continue;
        }
        // grab variable name (key)
        char *s=++p;
        #define KEYSIZE 1000;
        char key[KEYSIZE+1]; // you may want to dynamically allocate this (using malloc or strdup after grabbing the key);
        while(*s && *s!=')')
            s++;
        if(*s==0){ // didnt find closing ')'
            fputs("Unmatched '('", stderr);
            exit(-1);
        }
        if((s-p)>KEYSIZE){
            // string inside $() is longer than KEYSIZE. what do you want to do? I'll use just KEYSIZE chars.
            s=p+KEYSIZE;
        }
        strncpy(key,p,s-p);
        key[s-p]=0;
        p=s+1;
        // fetch the value for substitution
        for(int i=0; i<varCount;i++)
            if(!strcmp(variables[i].key, key)){
                fprintf(output,"%s", variables[i].data);
                break;
            }
    }
}

Обновление: убрать пробелы перед сохранением ключа:

    char* p = strstr(buffer , "=");
    if (p) {
        *p++ = 0;
       // **start of update**: Strip away surrounding blanks --------
         char *s=buffer;
         while (*s && *s== ' ') s++; // remove leading blanks
         if(*s){
               // Remove terminating blanks
               char *t=s;
                while(*t && *t!=' ') t++;
                *t=0;
          }
          memcpy(buffer, s, strlen(s));
       // **end of update** -- finished stripping surrounding blanks
        // If copy size is too small, change the final number in the function
        strncpy(variables[j].key, buffer,1000);
        strncpy(variables[j].data, p,1000);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...