2016-07-08 16 views
-2

Ich versuche zu üben, indem ich ein kleines, kurzes Programm schreibe, um eine Textdatei einzulesen und ihren Inhalt in ein Array zu legen. Ich wollte dieses Programm schreiben, um jede Textdatei als String einzulesen und in einem Array zu speichern. Auf diese Weise können Sie jede Datei lesen und unabhängig von der Länge der Zeichenfolge ein Array dynamisch erstellen und es mit einer Datei füllen. Ich benutze dies als eine Übung, um mit C zu üben und hoffe, dies auf andere Typen und Strukturen zu extrapolieren.C scanf string array

Aus irgendeinem Grund stimmt mein erster Eintrag nicht überein, was zu unerwartetem Verhalten führt, aber zumindest keine Seg-Fehler. Ich verstehe, dass mit C, müssen Sie im Wesentlichen Mikro alle Ihre Speicher verwalten, und arbeiten mit dem Code, ich habe versucht, Speicher für jeden Eintrag zu reservieren, aber ist dies der richtige Ansatz? Ich habe den Code durch meinen Kopf laufen lassen, und es macht logisch Sinn, wenn ich mit 0 Einträgen anfange, aber ich verstehe nicht, warum der erste Eintrag fehlschlägt, während die restlichen Einträge funktionieren.

Code:

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

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

    //Initialize variables and pointers 
    //Create an array of chars to use when reading in 
    //Create an array of strings to store 
    //i : use to keep track of the number of strings in array 
    //j : loop variable 
    //size: size of string 
    char *s = (char *) malloc(sizeof(char)); 
    int i=0,j=0; 
    int size = 0; 
    char **a = (char **) malloc(sizeof(char *)); 

    //Read in string, assign string to an address at a[] 
    while(scanf("%79s",s) == 1){ 

     //Get the size of the input string 
     size = (unsigned) strlen(s); 

     //Print some notes here 
     printf("\nString is \"%-14s\"\tSize is %-3d, i is currently %d\n",s,size,i); 
     printf("Using MALLOC with %d bytes\n",size+1); 

     //Allocate memory to copy string 
     // 
     //For some reason, the commented code works 
     //a[i] = (char *) (malloc(sizeof(char)*(size+1)) + 'a'); 
     a[i] = (char *) (malloc(sizeof(char)*(size+1))); 

     //Go and allocate memory for each character in string to store 
     for(j=0; j<(size+1); j++) a[i][j] = (char) (malloc(sizeof(char))); 

     //Print some more notes here 
     printf("Size: a[%2d] is %3d bytes, *a[%2d] is %3d bytes, Length of a[%2d] is %d\n",i,(int) sizeof(a[i]),i,(int) sizeof(*a[i]),i,(unsigned) strlen(a[i])); 

     //Copy over string and set last char to be end 
     for(j=0; j<size; j++)  a[i][j] = (char) s[j]; 
     a[i][size] = '\0'; 

     //Print it out and increase i 
     printf("a[%3d] is now %s\n",i,a[i]); 

     i++; 
    } 
    printf("I is now %d\n\n\n",i); 
    a[i] = NULL; 

    //print out array 
    for(j=0; j<i; j++)  printf("%3d. %-40s\n",j,a[j]); 


    return 0; 
} 

Test-Textdatei (numbers.txt):

1 
22 
333 
4444 
55555 
666666 
7777777 
88888888 
9999999 
0000000000 
11111111111 
222222222 

Befehl:

./a.out < numbers.txt

Ergebnisse:

String is "1    "  Size is 1 , i is currently 0 
Using MALLOC with 2 bytes 
Size: a[ 0] is 8 bytes, *a[ 0] is 1 bytes, Length of a[ 0] is 2 
a[ 0] is now 1 

String is "22   "  Size is 2 , i is currently 1 
Using MALLOC with 3 bytes 
Size: a[ 1] is 8 bytes, *a[ 1] is 1 bytes, Length of a[ 1] is 3 
a[ 1] is now 22 

String is "333   "  Size is 3 , i is currently 2 
Using MALLOC with 4 bytes 
Size: a[ 2] is 8 bytes, *a[ 2] is 1 bytes, Length of a[ 2] is 4 
a[ 2] is now 333 

String is "4444   "  Size is 4 , i is currently 3 
Using MALLOC with 5 bytes 
Size: a[ 3] is 8 bytes, *a[ 3] is 1 bytes, Length of a[ 3] is 5 
a[ 3] is now 4444 

String is "55555   "  Size is 5 , i is currently 4 
Using MALLOC with 6 bytes 
Size: a[ 4] is 8 bytes, *a[ 4] is 1 bytes, Length of a[ 4] is 6 
a[ 4] is now 55555 

String is "666666  "  Size is 6 , i is currently 5 
Using MALLOC with 7 bytes 
Size: a[ 5] is 8 bytes, *a[ 5] is 1 bytes, Length of a[ 5] is 7 
a[ 5] is now 666666 

String is "7777777  "  Size is 7 , i is currently 6 
Using MALLOC with 8 bytes 
Size: a[ 6] is 8 bytes, *a[ 6] is 1 bytes, Length of a[ 6] is 8 
a[ 6] is now 7777777 

String is "88888888  "  Size is 8 , i is currently 7 
Using MALLOC with 9 bytes 
Size: a[ 7] is 8 bytes, *a[ 7] is 1 bytes, Length of a[ 7] is 9 
a[ 7] is now 88888888 

String is "9999999  "  Size is 7 , i is currently 8 
Using MALLOC with 8 bytes 
Size: a[ 8] is 8 bytes, *a[ 8] is 1 bytes, Length of a[ 8] is 8 
a[ 8] is now 9999999 

String is "0000000000 "  Size is 10 , i is currently 9 
Using MALLOC with 11 bytes 
Size: a[ 9] is 8 bytes, *a[ 9] is 1 bytes, Length of a[ 9] is 11 
a[ 9] is now 0000000000 

String is "11111111111 "  Size is 11 , i is currently 10 
Using MALLOC with 12 bytes 
Size: a[10] is 8 bytes, *a[10] is 1 bytes, Length of a[10] is 12 
a[ 10] is now 11111111111 

String is "222222222  "  Size is 9 , i is currently 11 
Using MALLOC with 10 bytes 
Size: a[11] is 8 bytes, *a[11] is 1 bytes, Length of a[11] is 10 
a[ 11] is now 222222222 
I is now 12 


    0. ▒"▒ 
    1. 22 
    2. 333 
    3. 4444 
    4. 55555 
    5. 666666 
    6. 7777777 
    7. 88888888 
    8. 9999999 
    9. 0000000000 
10. 11111111111 
11. 222222222 
+5

Nicht definiertes Verhalten für die Verwendung des Werts eines Objekts mit automatischer Speicherdauer, während es nicht initialisiert ist. Undefiniertes Verhalten zum Übergeben eines falschen Argumenttyps an eine varargs-Funktion. Undefiniertes Verhalten für 'fflush()' 'einen nicht ausgegebenen Stream. – EOF

+3

Sie müssen mehr tun, als einen Zeiger zu deklarieren ... Sie müssen auch Speicher für die gelesenen Strings zuweisen (und das Array von Zeigern darauf). Möglicherweise müssen Sie die Größe dieser Zuordnungen auch ändern, wenn Sie nicht wissen, wie viel Speicherplatz Sie benötigen. – Dmitri

+0

Wenn Sie 'scanf' mit Strings verwenden, brauchen Sie nicht das' & '. Jedes Element in Ihrem Array 'a' ist eine Zeichenkette,' a [0] 'ist die erste' a [1] 'die zweite, usw. Wenn Sie eine Zeichenkette ausgeben, verwenden Sie einfach den _name_ des string/char-Arrays (Der Zeiger auf das erste Element im char-Array. Um also einen String ('char *') auszugeben, der ein Element im Array 'a' ist, würde man einfach 'printf' (meine erste Zeile ist:% s \ n ", a [0]);'. Außerdem würden Sie niemals '&' zum Ausdruck einer Zeichenkette (oder eines pointer-to-struct int, zB) verwenden, Sie würden stattdessen den Operator '*' benutzen, wenn nötig (aber wiederum nicht mit Strings, weil _pects_ der ptr selbst). – RastaJedi

Antwort

1

Für den Anfang: Ihre Deklaration char **a; deklariert einen Zeiger; vom richtigen Typ, aber nach nirgendwo zeigend.

Ihr aktueller Code schreibt in einen nicht definierten Bereich des Speichers, und das funktioniert manchmal und manchmal Dumps; aber die Wirkung ist unbestimmt und meist unvorhersehbar.

In C müssen Sie mit Ihrem eigenen Speicher umgehen. Entweder deklarieren Sie den Speicher, den Sie möchten, damit der Zeiger nach rechts zeigt: char a[255][10]; (für 255 Zeichen in jeder Zeichenfolge und 10 davon), oder Sie ordnen ihn auf dem Heap (mit malloc) zu und weisen ihn dem Zeiger zu : a = (char * *) malloc(sizeof(char *) * 10); (wieder um 10 zu bekommen), und dann für jeden von ihnen: a[i] = (char *) malloc(sizeof(char) * 255);.

Letzteres ermöglicht es Ihnen, Variablen für die Größen zu verwenden, wenn Sie möchten, und ermöglicht auch viel größere Speicherabschnitte auf die erste Weise.

Sobald Sie das verstanden haben, wenden Sie es auf die anderen Variablen an, die Sie verwenden möchten. Denken Sie daran, dass jeder Zeiger, den Sie deklarieren, genau das ist, ein Zeiger. Es ist Ihre Aufgabe, auf etwas Nützliches hinzuweisen und an diesem Ort Speicher bereitzustellen.

+0

'deklarieren Sie den Speicher, den Sie möchten, dass dieser Zeiger mit der Maus nach rechts zeigt: char a [10] [255]' - Nein, Sie deklarieren hier keinen Zeiger, Sie deklarieren ein Array von Stapeln (automatisch) zugewiesene Arrays. Schlimmer noch, es sind eigentlich 255 Arrays jeder Größe 10, nicht was du gesagt hast. –

+0

hat das Sequenzproblem korrigiert. Aber "a" ist hinterher ein Zeiger und kann als solcher verwendet werden. – Aganju

+0

Nein, 'a' ist der Name eines _value_. Die Tatsache, dass Array-Namen oft implizit in Zeiger umgewandelt werden, ist im Zusammenhang mit der Deklaration nicht direkt relevant, was Sie beschreiben. Die ersteren haben einige wesentliche Kräfte, die Zeiger nicht haben. Ja, Sie können dann Array-Namen an Funktionen übergeben, die Zeiger erwarten, auf die sie stillschweigend "abfallen", aber sie sind keine Zeiger. –

1

Mein Versuch einer der besten Möglichkeiten, dies zu implementieren, wäre die Verwendung von verketteten Listen; Jede Zeile würde als "Knoten" für die Liste betrachtet werden.

Ich schlage eine verkettete Liste vor, weil Sie anscheinend die Informationen unabhängig von der Anzahl der Zeilen in der Datei lesen möchten. Daher wäre ein dynamisch zugewiesenes Array nützlich, wie Sie die Nummern getrennt in einem Array speichern.

Unabhängig von der Anzahl der Zeilen werden die Informationen in der verknüpften Liste gespeichert (vorausgesetzt, auf Ihrem Computer ist genügend Speicherplatz verfügbar).

Code:

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

#define FILENAME file.txt 
//While the amount of chars in each line is limited, are you really going to use more than 1000 per line? 
#define MAX_LINE_SIZE 1000 
struct node { 
    char data[MAX_LINE_SIZE]; 
    struct node* next; 
}; 


void insert_node (struct node **head, char *nodeValue, size_t line_max_size); 
void print_list (struct node *head); 

int main(int argc, char const *argv[]) { 
    //Open the file 
    FILE *file_to_open; 
    file_to_open = fopen("numbers.txt", "r"); 
    if(!file_to_open) { 
    return 1; 
    } 
    char currentLine[MAX_LINE_SIZE] = {0}; 

    struct node* headNode; 
    headNode = NULL; //Initialize our first node pointer to be NULL 

    while (fgets(currentLine, sizeof(currentLine), file_to_open)) { 
    insert_node(&headNode, currentLine, sizeof(currentLine)); 
    } 

    printf("Array list from your file:\n"); 
    print_list(headNode); 

    fclose(file_to_open); 
    return 0; 
} 

void print_list (struct node *head) { 
    //We define a new 'currentNode' instead of just using headNode so we don't modify the headNode 
    struct node* currentNode = head; 

    while (currentNode != NULL) { 
     printf("Value: %s", currentNode->data); 
     currentNode = currentNode -> next; 
    } 
} 

void insert_node (struct node **head, char *nodeValue, size_t line_max_size) { 
    struct node *currentNode = malloc(sizeof(struct node)); 

    strncpy(currentNode->data, nodeValue, line_max_size); 
    currentNode->next = *head; 

    /* 
    * In order to understand how we add nodes, let's take a look at possible senarios: 
    * 1. The list is empty, so we need to add a new node 
    * In which case, our memory looks like this where HEAD is a pointer to the first node: 
    * ----- 
    * |HEAD | --> NULL 
    * ----- 
    * The line currentNode->next = headNode; will NOT do anything since headNode originally 
    * starts out at a value of NULL 
    * Now, we want to set our current node to be the (new) head node 
    * -----  ------------- 
    * |HEAD | --> |CURRENTNODE| //The head node points to the current node 
    * -----  ------------- 
    * This is done with headNode = currentNode; (shown below) 
    * So, headNode now points directly to the node structure we just created 
    * 
    * 2. The list is already populated; we need to add a new node to the beginning 
    * For the sake of simplicity, let's start out with 1 node: 
    * ------ -------------------- 
    * HEAD -> FIRST NODE --> NULL 
    * ------ -------------------- 
    * With currentNode->next = headNode, the data structure looks like this: 
    * ---------  ----- --------------------- 
    * currentNode --> HEAD -> POINTER TO FIRST NODE 
    * ---------  ----- --------------------- 
    * Which, obviously needs to be altered since the currentNode should then 
    * become the first node & the head should point to the first node: 
    * ---- ---    ---------------------------- 
    * HEAD -> currentNode -->   2nd NODE 
    * ----- --    ---------------------------- 
    * This is done with head = currentNode 
    * Note: In this explanation, I did not explain *head = currentNode like in the actual code 
    * This was to make the explanation simpler, although we do need to 
    * dereference once to access head through the pointer to pointer 
    */ 
    *head = currentNode; 
} 

Ich versuchte zu erklären, wie ich Knoten in einer verknüpften Liste nach bestem meine Fähigkeit, in den Kommentaren eingefügt, sondern Klärung dieser Fragen können immer online.

Die Ausgabe des Programms sieht wie folgt aus:

 
    Array list from your file: 
    Value: 222222222 
    Value: 11111111111 
    Value: 0000000000 
    Value: 9999999 
    Value: 88888888 
    Value: 7777777 
    Value: 666666 
    Value: 55555 
    Value: 4444 
    Value: 333 
    Value: 22 
    Value: 1 

Technisch jede Zeile nicht mehr als 1000 Zeichen in diesem Programm sein kann, aber es ist selten mehr pro Zeile zu müssen.

Beachten Sie auch, dass dieses Programm die verkettete Liste (und die Reihenfolge numbers.txt) rückwärts ausgibt, da wir fortlaufend Knoten am Anfang der Liste hinzufügen. Es sollte jedoch relativ einfach sein, die Reihenfolge der verknüpften Liste umzukehren.