c-为什么我在流编辑器程序中遇到较大输入大小的分段错误



我调试了下面的代码,注意到我的editFileToStdIn抛出了分段错误。解决这个问题的最佳方法是什么?您可以通过使用gcc进行编译并像./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] = '';
}
strcat(text, val.data);
strcpy(line, text);
break;
case 'd':
line[0] = '';
break;
case 'I':
strcpy(text, val.data);
length = strlen(text) - 1;
if (text[length] == 'n') {
text[length] = '';
}
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, "");
// 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);
}
}

我强烈建议您在编译时开始使用一些标志。如果您将警告作为错误启用,gcc将不允许您在不解决它们的情况下进行编译。

$ 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

一旦你输入"运行",你就可以准确地看到分段故障在哪里

如果你尝试了这个并得到了一些关于错误的信息,请告诉我它说了什么,这样我就可以尝试帮助你更多。

希望它能有所帮助!

最新更新