2010-12-04 8 views
1

Meine Hausaufgabe ist es, eine Shell (done) mit einer History-Funktion (fertig) zu erstellen und dann den Verlauf in eine Datei schreiben (das ist, wo ich Probleme habe), so dass Es kann beim nächsten Lauf geladen werden.Schreiben einer Zeichenfolge in eine Datei führt zu seltsamen Zeichen

Ich bin normalerweise eine Java-Person, also muss ich fast jede c-Funktion nachschlagen. Wie auch immer, ich habe Probleme, mein String-Array in die Datei zu schreiben. Aus irgendeinem Grund bekomme ich seltsame Symbole, je nachdem, welche Kodierung ich verwende. Zum Beispiel, wenn ich das Programm laufen lassen und sofort beenden, die History-Datei enthält:

??_?

Wenn ich ls beenden Sie laufen, die History-Datei enthält:

ls 
_?

ich die Verfahren entfernt, die aren nicht verwandt. Vielen Dank im Voraus für alle Hinweise. Dies ist Tag 3 des Hairpulling. Ich bin mit meinem Latein am Ende.

#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 */ 
... 
}

Dump der Geschichte [] []:

./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 
_? 
??_? 

Der Rest meines Codes. loadHistory und saveHistory sind vor diesem:


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

Ihr Code-Dump scheint immer noch fehlerhaft zu sein. Bitte lesen Sie die [Markdown-Bearbeitungshilfe] (http://stackoverflow.com/editing-help). –

+0

Es gibt einen Fehler in Ihrer for-Schleife. Versuchen Sie es mit & lt; anstelle von < wird das Symbol nicht als Markup interpretiert. – Heatsink

+0

Könnten Sie bitte versuchen, den Inhalt von 'history' zu löschen? Ich vermute, dass Sie den Verlaufspuffer nicht bereinigen, daher gibt es dort einige beliebige Zeichen. – Vlad

Antwort

3

Das Problem könnte sein, dass Sie den Speicher in history vor der Verwendung nicht initialisieren. Im Gegensatz zu Java initialisiert C den Speicher nicht, wenn Sie es deklarieren, sodass der Zeiger history auf den Speicherbereich und nicht auf einen Bereich von Nullen zeigt.

C hat ein sehr unterschiedliches Speichermodell zu Java, so dass es im Rest Ihres Codes wahrscheinlich ähnliche Fehler gibt. Ich würde empfehlen, dass Sie lesen, wie Sie Speicher in C verwalten.

+0

Danke. Ich lese jetzt über Speicherverwaltung. – MikeKusold

+0

Ich konnte nicht herausfinden, wie man Speicher für ein Array von Strings reserviert. Ich habe versucht: history = (char (*) [BUFFER_SIZE]) malloc (HIST_SIZE * BUFFER_SIZE * sizeof (char)); Der Compiler beklagt sich jedoch darüber. – MikeKusold

+0

Ihre Speicherzuweisung ist OK; Es fehlt nur die Initialisierung. Nachschlagen 'memset';) –

-1

Sie haben eine harte Zeit haben die Menschen immer für Sie Ihre Hausaufgaben zu beheben. Lernprogrammieren bedeutet, deinen Kopf ständig gegen die Wand zu schlagen und es erneut zu versuchen, auch wenn es sich hoffnungslos anfühlt. Gib nicht auf.

+1

Ich möchte definitiv nicht, dass Leute meine Hausaufgaben machen. Ich würde nur gerne wissen, wie man die seltsamen Charaktere, die erscheinen, los wird. Ich habe mehrere Möglichkeiten ausprobiert, um in eine Datei zu schreiben und bekomme immer noch das gleiche Ergebnis. – MikeKusold

1

Bei der ersten Ausführung, wenn die Verlaufsdatei nicht erstellt wurde, laden Sie nicht den Verlauf. Allerdings scheinen Sie das history Array nicht zu bereinigen (die Standardwerte sind nicht ganz Nullen, im Gegensatz zu Java), also enthält es etwas Müll. Am Ausgang, wenn Sie Ihre Werte ausschreiben, legen Sie in die History-Datei den Müll und lesen ihn dann zurück.

Sie sollten sicherstellen, dass alle Ihre Zeichenfolgen NULL-terminiert sind. Immer.


Bitte versuchen Sie folgendes zu tun:

  1. Löschen Sie die vorhandene Protokolldatei
  2. Zu Beginn des main initialisieren history so dass jede Zeile am Anfang NULL enthält
  3. Jedes Mal, wenn die Speicherung Stellen Sie sicher, dass Sie entweder einige standardmäßige Zeichenfolgenkopierfunktionen (wie strcpy) verwenden, oder kopieren Sie den abschließenden NULL ebenfalls.

By the way, die Prüfung if(history[i] != '\0') ist falsch, wie history[i] verweist auf den bereits zugewiesenen Teil der Geschichte Puffer.Wollte man if (history[i][0] == '\0') setzen (was prüfen würde, ob die i-te History-Zeile leer ist)?

Nun, feste Größen von Puffern sind böse, aber das ist eine andere Geschichte.


Zum neu geposteten Code: Es gibt überall massive Probleme.

Einer von ihnen: die Überprüfung (strcmp(inputBuffer, "r\n\0") != 0) && (strncmp(inputBuffer, "r x", 2) != 0) in setup ist völlig falsch. Zuerst müssen Sie nicht explizit \0 hinzufügen, da jedes Zeichenfolgenliteral implizit durch \0 endet. Zweitens: Der erste Teil (strcmp(inputBuffer, "r\n\0") != 0) prüft, ob der Inhalt inputBufferr\n ist, und der zweite Teil (strncmp(inputBuffer, "r x", 2) != 0) prüft, ob inputBuffer mit r x beginnt. Offensichtlich schließen sich diese beiden Bedingungen gegenseitig aus, wenn Sie also && zwischen sie setzen, wird das Ergebnis niemals wahr sein.

Viele Code-Konstrukte in dem angegebenen Code tun nicht, was Sie zu beabsichtigen scheinen. Ich schlage vor, dass Sie langsam durch den Code im Debugger gehen, jede Variable und jedes Ergebnis des Funktionsaufrufs untersuchen, ich wette, Sie werden überrascht sein.

Eines der größten Probleme mit dem Code ist die Arbeit mit Strings und die allgemeine Speicherverwaltung. Bitte lesen Sie etwas zu diesem Thema: Es ist äußerst wichtig für die C-Programme. Ein paar Hinweise:

  1. Pointer zeigt auf einige rohe Speicher. Der Compiler prüft nicht, ob der Speicher unter dem Zeiger char* wirklich char s enthält oder nicht.
  2. Um es noch schlimmer zu machen, gibt es eine Konvention, dass Zeichenfolge ist der gleiche wie Zeiger auf sein erstes Zeichen. Das Ende der Zeichenfolge ist immer 0. Es gibt keine andere Möglichkeit, das Ende der Zeichenfolge zu bestimmen. Wenn Sie die abschließende 0 irgendwie überschreiben, wird das Ende der Zeichenfolge bei der nächsten 0 im Speicher sein.
  3. Es ist normalerweise unpraktisch, mit Zeichenfolgen auf Einzelzeichenebene zu arbeiten. Es gibt zahlreiche Utility-Funktionen wie strcmp oder strcpy, die dies für Sie tun können. Normalerweise sind diese Funktionen effizienter und haben keine Fehler.
  4. Ich kann immer noch nicht den Code in setup finden, die die Daten in den Verlauf legen. Es gibt einen Teil mit dem Kommentar "überprüft, ob der Befehl ein History Retrieval-Befehl ist. Wenn es nicht ist, dann fügen Sie es dem Verlauf hinzu", aber dieser Teil tut es nicht (teilweise weil es nie ausgeführt wird wegen der falscher Check am Anfang, teilweise weil der Code darin nichts kopiert von der inputBuffer, nur zu it).
+0

I NULL-terminiert die Zeichenfolgen in setup(), die ich nicht eingeschlossen habe. – MikeKusold

+0

@Vasto: Könnten Sie bitte Ihren Code posten, der das tut? Es könnte einen Fehler enthalten. – Vlad

+0

@Vasto: und vielleicht könnten Sie auch den Code schreiben, der die Werte in der Historie speichert. – Vlad

Verwandte Themen