2010-12-04 3 views
1

Мое домашнее задание состоит в том, чтобы создать оболочку (выполненную) с помощью функции истории (сделанной), а затем записать историю в файл (вот где у меня возникают проблемы), так что его можно загрузить при следующем запуске.Запись строки в файл приводит к появлению странных символов

Я обычно человек Java, поэтому мне нужно искать почти каждую функцию c. В любом случае, у меня возникают проблемы, напишите мой массив строк в файл. По какой-то причине я продолжаю получать странные символы в зависимости от используемой кодировки. Например, если я запускаю программу и выйти сразу, файл истории содержит:

??_?

Если я бегу Ls затем бросить курить, файл истории содержит:

ls 
_?

я удалил процедуры, которые Арен Связанный. Спасибо заранее за любые указатели. Это 3-й день причесок. Я нахожусь на своем пути.

#include 
/*Had to use \ so that the includes would show here. */ 
#include \stdlib.h> 
#include \signal.h> 
#include \sys/types.h> 
#include \unistd.h> 
#include \string.h> 
#include \errno.h> 
/* 
* Constant Declarations 
*/ 
#define MAX_LINE 80 
#define BUFFER_SIZE 50 
#define HIST_SIZE 10 

static char buffer[BUFFER_SIZE]; 
char history[HIST_SIZE][BUFFER_SIZE]; 
int count = 0; 
int caught = 0; 
char historyFileLoc[] = "./name.history"; 

void loadHistory() { 
    int i; 
    char histCommand[BUFFER_SIZE]; 

    i = 0; 
    FILE *hisFile = fopen(historyFileLoc, "r"); 

    if(hisFile) { 
     /*If a user edits the history file, only the first ten entries will be loaded */ 
     while(!feof(hisFile)) { 
      if(fscanf(hisFile, "%s\n", histCommand) == 1){ 
       strcpy(history[i], histCommand); 
       i++; 
       count++; 
      } 
     } 
    } 

    if(hisFile != NULL){ 
     if(fclose(hisFile) != 0) { 
      perror("History file (r) was not closed correctly"); 
     } 
    } 
} 

void saveHistory() { 
    int i; 
    char buffer[MAX_LINE]; 

    FILE *hisFile = fopen(historyFileLoc, "w"); 

    for(i=0; i < HIST_SIZE; i++){ 
     if(history[i] != '\0') { 
      strcpy(buffer, history[i]); 
     /*fwrite(history[i], 1, strlen(history[i]), hisFile);*/ 
     /* fputs(history[i], hisFile);*/ 
      if(buffer != NULL) { 
       fprintf(hisFile,"%s\n", buffer); 
      } 
     } 
    } 
    if(fclose(hisFile) != 0) { 
    perror("History file was not closed correctly"); 
    } 
} 


/* 
* The main() function presents the prompt "sh>" and then invokes setup(), which waits for the 
* user to enter a command. The contents of the command entered by the user are loaded into the 
* args array. For example, if the user enters ls Ðl at the COMMAND-> prompt, args[0] will be set 
* to the string ls and args[1] will be set to the string Ðl. (By ÒstringÓ, we mean a 
* null-terminated, C-style string variable.) 
*/ 
int main(void) { 
    char inputBuffer[MAX_LINE]; /* buffer to hold the command entered */ 
    int background, status; /* equals 1 if a command is followed by '&' */ 
    char *args[MAX_LINE/2 + 1]; /* command line arguments */ 
    pid_t pid; /* the process's id */ 
... 
}

Свалка истории [] []:

./660_Lab04.c.out 

sh ->ls 
660_Lab03_Tests.txt    Lab 03 Documentation.docx 
660_Lab04.c     buffer.h 
660_Lab04.c.out     name.history 
660__Lab01.c     main.c 
660__Lab03.c     main_not_mine.c 
CSE_660Lab01Documentation.doc 

This is where the dump begins minus sh-> 
sh ->ls 
ls 
_? 
??_? 

Остальная часть моего кода. loadHistory и saveHistory являются до этого:


void printHistory() { 
    int i; 
    int j = 0; 
    int histcount = count; 

    printf("\n"); 
    for (i=0; i < HIST_SIZE; i++) { 
     printf("%d. ",histcount); /* Used to print the correct hitory number */ 
     while (history[i][j] != '\n' && history[i][j] != '\0') { 
      printf("%c",history[i][j]); 
      j++; 
     } 
     printf("\n"); 
     j=0; 

     histcount--; 
     if(histcount == 0) { 
      break; 
     } 
    } 
    printf("\n"); 
    printf("sh -> "); 
} 

/* the signal handler function */ 
void handle_SIGINT() { 
    write(STDOUT_FILENO,buffer,strlen(buffer)); 
    printHistory(); 
    caught = 1; 
} 

void setup(char inputBuffer[], char *args[], int *background) { 
    int length, /* # of characters in the command line */ 
     i, /* loop index for accessing inputBuffer array */ 
     start, /* index where beginning of next command parameter is */ 
     ct, /* index of where to place the next parameter into args[] */ 
     k; /* Generic counter */ 

    ct = 0; 

/* read what the user enters on the command line */ 
    length = read(STDIN_FILENO, inputBuffer, MAX_LINE); 

    if(caught == 1) { 
     length = read(STDIN_FILENO, inputBuffer, MAX_LINE); 
     caught = 0; 
    } 

/* checks to see if the command is a history retrieval command. If it isn't then add it to the history */ 
    if((strcmp(inputBuffer, "r\n\0") != 0) && (strncmp(inputBuffer, "r x", 2) != 0)) { 
     for(i= (HIST_SIZE - 1); i>0; i--) { 
      strcpy(history[i], history[i-1]); 
     } 
     strcpy(history[0], inputBuffer); 
     count++; 
    } 
    start = -1; 
    if (length == 0) { 
     saveHistory(); 
     exit(0); /* ^d was entered, end of user command stream */ 
    } else if ((length < 0) && (errno != EINTR)) { 
     perror("error reading the command"); 
     saveHistory(); 
     exit(-1); /* terminate with error code of -1 */ 
    } 

/* Checks to see if r was entered. If so, it copies the command most recently in the input buffer */ 
    if(strcmp(inputBuffer, "r\n\0") == 0) { 
     strcpy(inputBuffer,history[0]); 
     /* Checks to see if r x was entered. If so then it searches for the most recent command that begins with x */ 
     } else if(strncmp(inputBuffer, "r x", 2) == 0) { 
      for(k=0; k < 10; k++){ 
       if(inputBuffer[2] == history[k][0]) { 
        strcpy(inputBuffer,history[k]); 
        break; 
       } 
      } 
     } 

     length = strlen(inputBuffer); 

/* examine every character in the inputBuffer */ 
     for (i = 0; i < length; i++) { 
      switch (inputBuffer[i]) { 
       case ' ': 
       case '\t': /* argument separators */ 
       if (start != -1) { 
        args[ct] = &inputBuffer[start]; /* set up pointer */ 
        ct++; 
       } 
       inputBuffer[i] = '\0'; 
       start = -1; 
       break; 

       case '\n': /* should be the final char examined */ 
       if (start != -1) { 
        args[ct] = &inputBuffer[start]; 
        ct++; 
       } 
       inputBuffer[i] = '\0'; 
       args[ct] = NULL; /* no more arguments to this command */ 
       break; 
       case '&': 
       *background = 1; 
       inputBuffer[i] = '\0'; 
       break; 

       default: /* some other character */ 
       if (start == -1) { 
        start = i; 
       } 
      } 
      args[ct] = NULL; /* just in case the input line was > 80 */ 
     } 
    } 


/* The main() function presents the prompt "sh->" and then invokes setup(), which waits for the user to enter a command. The contents of the command entered by the user are loaded into the args array. For example, if the user enters ls -l at the COMMAND-> prompt, args[0] will be set to the string ls and args[1] will be set to the string -l. (By string, we mean a null-terminated, C-style string variable.) */ 
int main(void) { 
    char inputBuffer[MAX_LINE]; /* buffer to hold the command entered */ 
    int background, status; /* equals 1 if a command is followed by '&' */ 
    char *args[MAX_LINE/2 + 1]; /* command line arguments */ 
    pid_t pid; /* the process's id */ 

/* set up the signal handler */ 
    struct sigaction handler; 
    handler.sa_handler = handle_SIGINT; 
    sigaction(SIGINT, &handler, NULL); 

    loadHistory(); 

    while (1) { 
     background = 0; 

     printf("\nsh ->"); 
     fflush(0); 
     setup(inputBuffer, args, &background); /* get next command */ 
     fflush(0); 
     pid = fork(); /* assign the process id */ 
     if (pid < 0) { 
      fprintf(stderr, "ERROR: Could not properly fork."); 
      saveHistory(); 
      exit(-1); /* unsucessful exit because the fork could not be created */ 
     } else if (pid == 0) { /* PID was forked successfully */ 
      status = execvp(*args, args); /* execute the command */ 
      if (status < 0) { 
       fprintf(stderr, "ERROR: Could not execute %s", args[0]); 
       saveHistory(); 
       exit(1); 
      } 
     } else if (background == 0) { /* if the fork is run in the foreground */ 
      wait(NULL); 
     } 
    } 

    return EXIT_SUCCESS; 
} 
+0

Сброс кода по-прежнему выглядит сломанным. Пожалуйста, прочитайте [Редактирование редактирования Markdown] (http://stackoverflow.com/editing-help). – 2010-12-04 00:37:20

+0

В вашем цикле for есть ошибка. Попробуйте ввести & lt; вместо <, поэтому символ не интерпретируется как разметка. – Heatsink 2010-12-04 00:38:07

+0

Не могли бы вы попытаться сбросить содержимое «истории»? Я подозреваю, что вы не очищаете буфер истории, поэтому там есть некоторые произвольные символы. – Vlad 2010-12-04 00:41:01

ответ

3

Проблема может заключаться в том, что вы не инициализируете память в history перед использованием. В отличие от Java, C не инициализирует память, когда вы ее объявляете, поэтому указатель history будет указывать на мусор, а не на массив нулей.

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

-1

Вы будете иметь трудное время, чтобы заставить людей исправить свою домашнюю работу для вас. Обучение программированию - это постоянно размахивать головой о стену и повторять попытку, даже несмотря на то, что она кажется безнадежной. Не сдавайся.

1

В самом первом запуске, когда файл истории не был создан, вы не загружаете историю. Однако вы, кажется, не очищаете массив history (значения по умолчанию не все нули, в отличие от Java), поэтому он содержит мусор. На выходе, когда вы пишете свои значения, вы помещаете в файл истории мусор, а затем читаете его.

Вы должны убедиться, что все ваши строки завершены с нулевым значением. Всегда.


Пожалуйста, попробуйте сделать следующее:

  1. Удалить существующий файл истории
  2. В начале main, инициализировать history так, что каждая строка содержит NULL в начале
  3. Всякий раз, когда хранение что-либо в истории, убедитесь, что вы либо используете некоторые стандартные функции копирования строк (например, strcpy), либо копируете завершающий NULL.

Кстати, проверка if(history[i] != '\0') это не так, как history[i] указывает на уже выделенной части буфера истории.Вы хотите поставить if (history[i][0] == '\0') (что бы проверить, не ли ли яная строка истории)?

Ну, фиксированные размеры буферов являются злыми, но это уже другая история.


К недавно опубликованному коду: повсеместно возникают массивные проблемы.

Один из них: чек (strcmp(inputBuffer, "r\n\0") != 0) && (strncmp(inputBuffer, "r x", 2) != 0) в setup является абсолютно неправильным. Во-первых, вам не нужно явно указывать \0, потому что каждый строковый литерал заканчивается неявно \0. Второе: первая часть (strcmp(inputBuffer, "r\n\0") != 0) проверяет, соответствует ли inputBufferr\n, а вторая часть (strncmp(inputBuffer, "r x", 2) != 0) проверяет, начинается ли inputBuffer с r x. Ясно, что эти два условия являются взаимоисключающими, поэтому, если вы ставите && между ними, результат никогда не будет правдой.

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

Одна из самых больших проблем с кодом - работа со строками и общее управление памятью. Пожалуйста, прочитайте что-нибудь по этой теме: это жизненно важно для программ C. Несколько примечаний:

  1. Указатель указывает на некоторую необработанную память. Компилятор не проверяет, содержит ли память под указателем char*char или нет.
  2. Чтобы сделать это хуже, существует соглашение о том, что строка такая же, как указатель на ее первый символ. Конец строки равен 0, всегда. Других средств для определения конца строки нет. Если вы каким-то образом перезаписываете завершающий 0, конец строки будет в следующем 0 в памяти.
  3. Обычно нецелесообразно работать со строками на одном уровне персонажа. Существует множество полезных функций, таких как strcmp или strcpy, которые могут сделать это за вас. Обычно эти функции более эффективны и не имеют ошибок.
  4. Я до сих пор не могу найти код в setup, который помещает данные в историю. Существует часть комментария «проверки», чтобы проверить, является ли команда командой поиска истории. Если она не добавляет ее в историю », но эта часть этого не делает (частично потому, что она никогда не выполняется из-за неправильная проверка в начале, частично потому что код внутри ничего не копирует отinputBuffer, только до it).
Смежные вопросы