Почему я получаю ошибку сегментации в моей программе Stream Editor для больших размеров ввода? - PullRequest
1 голос
/ 07 мая 2020

Я отладил приведенный ниже код и заметил, что мой editFileToStdIn выдает ошибку сегментации. Как лучше всего решить эту проблему? Вы можете протестировать код в своем терминале, скомпилировав его с помощью g cc и запустив его так ./svi command.txt < example.txt. Мой код работает для меньших входных данных, но дает ошибку сегментации для больших входных данных, например: CommandFile и ExampleFile

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

#define ARRAY_SIZE 100
typedef char String[256];

//SVI Data Structures
//-----------------------------------------------------------------//
typedef enum {none,text,range} lineRangeSpec; //Range specification

// Union Structure for range specification
typedef union { 
    int none;
    String text;
    int range[2];
} lineRangeUnion;

//Edit Operators
typedef struct {
    lineRangeSpec specType;
    lineRangeUnion specifier;
    char editOperation; //will be one of {A,I,O,d,s}
    /*A - Appends the <text> at the end of the line
      I - Inserts the <text> at the start of the line.
      O - Inserts the <text> on a new line before the current line
      d - Deletes the line from the file.
      s - Replaces the first occurence of <old text>, in the line, with <new text>
  */
    String data; //Stores all text after edit operators have been taken
} EditOperator;

//-----------------------------------------------------------------//

//Read and store edit commands from file
//Read a line from standard input
bool isEditPossible(EditOperator edit, String currentLine, int lineNumber);
void editFile (EditOperator edit, String line);
int readEditFile (FILE * commands, EditOperator inputs[]);

//Synthesize Text based on Operation Specified
EditOperator editEverywhere (String command);
EditOperator editLineRange(String command);
EditOperator editText (String command);
EditOperator transformStruct(String input);
void swapDelimiters (String data, String former, String latter);
void swapText (EditOperator edit, String line);

//Output the edited line (unless it has been deleted)
void editFileToStdIn (EditOperator edits[], int length);

//Driver Code
int main (int argc, char * argv[]) {
    FILE * commands;
    int length;
    EditOperator values[ARRAY_SIZE];

    if (argc < 2) {
        printf("Error! Include a file name. e.g., `./svi a.txt >  b.txt `\n");
        return -1;
    } else if ((commands = fopen(argv[1], "r")) == NULL) {
        printf("File could not be opened. Program has ended.\n");
        return -1;
    } else {
        length = readEditFile(commands, values);
        editFileToStdIn(values, length);
        return 0;
    }
}

void editFile (EditOperator val, String line){
  String text;
  int length;
  switch(val.editOperation){
    case 'A':
            strcpy(text, line);
            length = strlen(text) - 1;
            if (text[length] == '\n') {
                text[length] = '\0';
            }
            strcat(text, val.data);
            strcpy(line, text);
            break;
        case 'd':
            line[0] = '\0';
            break;
        case 'I':
            strcpy(text, val.data);
            length = strlen(text) - 1;
            if (text[length] == '\n') {
                text[length] = '\0';
            }
            strcat(text, line);
            strcpy(line, text);
            break;
        case 'O':
            printf("%s", val.data);
            break;
        case 's':
            swapText(val, line);
            break;
        default:
            printf("Irregularly edited input. Program terminated \n");
            exit(-1);
            break;
  }
}

void editFileToStdIn (EditOperator edits[], int length){
  String input;
    int currentLine = 0, i;
    while (fgets(input, 256, stdin)) {
        currentLine += 1;
        for (i = 0; i < length; i++) {
            if (isEditPossible(edits[i], input, currentLine)) {
                editFile(edits[i], input);
                // if the edit says to delete this line, immediately break
                // so that other operations cannot be applied 
                if (edits[i].editOperation == 'd') {
                    break;
                }
            }
        }
        printf("%s", input);
}
}

void swapDelimiters (String data, String former, String latter){
  String value;
    char * delimiter = "/";
    char * token;
    strcpy(value, data);
    token = strtok(value, delimiter);
    strcpy(former, token);
    token = strtok(NULL, delimiter);
    strcpy(latter, token);
}

bool isEditPossible(EditOperator edit, String currentLine, int lineNumber){
   if (edit.specType == range) {
        return edit.specifier.range[0] <= lineNumber && lineNumber <= edit.specifier.range[1];
    } else if (edit.specType == text) {
        return strstr(currentLine, edit.specifier.text) != NULL;
    } else {
        return true;
    }
}

int readEditFile (FILE * commands, EditOperator inputs[]){
  String line;
    int i = 0; 
    while (i < 100 && fgets(line, 256, commands) != NULL) {
        inputs[i] = transformStruct(line);
        i += 1;
    }
    // close the file since it is no longer needed
    if (fclose(commands) == EOF) {
        perror("Unable to close file. Exiting program.");
        exit(-1);
    } else {
        return i;
}
}

EditOperator editLineRange(String command){
   EditOperator s;
    String copy;
    char * delimiters = ",/";
    char * delimiter = "/";
    char * token;

    s.specType = range;
    strcpy(copy, command);
    token = strtok(copy, delimiters);
    s.specifier.range[0] = atoi(token);
    token = strtok(NULL, delimiters);
    s.specifier.range[1] = atoi(token);
    token = strtok(NULL, delimiter);
    s.editOperation = token[0];
    strcpy(s.data, token + 1);
    return s;
}

EditOperator editText (String command) {
    EditOperator s;
    String copy;
    char * delimiter = "/";
    char * token;

    s.specType = text;
    strcpy(copy, command);

    // split string at first instance of '/'
    token = strtok(copy, delimiter);
    // and copy this into `Edit.rule.text`
    strcpy(s.specifier.text, token);

    // grab everything after the first slash to the null-terminator character
    token = strtok(NULL, "\0");
    // the first character is the edit type
    s.editOperation = token[0];
    // everything else is `Edit.data`
    strcpy(s.data, token + 1);

    return s;
}

EditOperator makeEverywhereEdit (String command) {
    EditOperator s;

    s.specType = none;
    s.specifier.none = true;
    strcpy(s.data, command + 1);
    s.editOperation = command[0];

    return s;
}

EditOperator editEverywhere (String command){
  EditOperator val;
  val.specType = none;
  val.specifier.none = true;
  strcpy(val.data, command + 1);
  val.editOperation = command[0];
  return val;
}

void swapText (EditOperator edit, String line) {
    String copy, replace, replaceWith;
    char * token;
    int i, delta, lengthOfReplace, lineLength;

    swapDelimiters(edit.data, replace, replaceWith);

    lengthOfReplace = strlen(replace);

    // in case there's garbage in `copy`
    strcpy(copy, "");

    // while line contains the text to be replaced
    while ((token = strstr(line, replace)) != NULL) {
        // let `delta` be the number of characters between the beginning of `line`
        // and the character at which `replace` was found in `line`
        delta = token - line;
        lineLength = strlen(line);

        // copy the `delta` characters of line before `replace` to `copy`
        strncat(copy, line, delta);
        // append `replaceWith` to `copy`
        strcat(copy, replaceWith);

        // shift the entire array forward, from the first character after the end of `replace`
        for (i = 0; i < lineLength - delta - lengthOfReplace + 1; i++) {
            line[i] = line[i + delta + lengthOfReplace];
        }
    }
    // while loop terminates when `replace` is no longer in `line`;
    // copy remaining character of `line` after last occurrence of `replace` to copy
    strcat(copy, line);
    // move `copy` to `line` (since it was passed by reference)
    strcpy(line, copy);
}

EditOperator transformStruct(String input){
    if ('0' <= input[0] && input[0] <= '9') {
        return editLineRange(input);
    } else if (input[0] == '/') {
        return editText(input);
    } else {
      return editEverywhere(input);
    }
}

1 Ответ

1 голос
/ 07 мая 2020

Я настоятельно рекомендую вам начать использовать некоторые флаги при компиляции. Если вы включите предупреждения как ошибки, g cc не позволит вам скомпилировать, не решив их.

$ gcc -Wall -Werror -Wextra -pedantic -std=c99 -c file_name.c
$ gcc -Wall -Werror -Wextra -pedantic -std=c99 -o program file_name.o
$ ./program

Это покажет вам ваши ошибки, а также позволит избежать компиляции, пока вы их не исправите.

Также вам следует попробовать отладчик:

Возможно, вы захотите использовать и исследовать gdb в будущем, когда у вас возникнут ошибки сегментации. Просто добавьте флаг -g в команду компиляции.

$ gcc -Wall -Werror -Wextra -pedantic -std=c99 -g -c file_name.c
$ gcc -Wall -Werror -Wextra -pedantic -std=c99 -g -o program file_name.o
$ gdb ./program

Как только вы наберете «запустить», вы сможете точно увидеть, где именно произошла ошибка сегментации.

Если вы попробуете это и получить некоторую информацию об ошибке, дайте мне знать, что там написано, чтобы я мог попытаться вам помочь.

Надеюсь, это поможет!

...