C Problem mit getline und array

Ramm

Lt. Junior Grade
Registriert
Mai 2005
Beiträge
377
Hallo,
Ich möchte jede Zeile der Datei list in ein Array schreiben. Doch irgendwie klappt das nicht.

Beispiel:

1. Zeile der Datei: a
2. Zeile der Datei: b

Innerhalb der while Schleife wird url mit line initialisiert. Printf gibt direkt nach der Initialisierung die Werte für url aus:

url[0] = a
url[1] = b

Allerdings benötige ich diese Werte nochmals außerhalb der Schleife und dann sind url[0] und url[1] gleich b:

url[0] = b
url[1] = b

Egal wie viel Zeilen die Datei hat am Ende hat. Der url Array nur die Werte der letzten Zeile.
Ich hoffe ihr könnt mir helfen.

Code:
#include<stdio.h>
#include<stdlib.h>

int main(void) {
	size_t *len = malloc(0);
	char *line = (char *) malloc(sizeof(char));
	line = NULL;

	FILE *fp_url_list;

	char **url = (char **) malloc(1000);
	short i = 0, line_length;

	fp_url_list = fopen("list", "r");

	while((line_length = getline(&line, len, fp_url_list)) > 0) {
		if(line[line_length - 1] == '\r' || line[line_length - 1] == '\n') {
			line[line_length - 1] = '\0';
		}

		url[i] = line;

		printf("url[%d] = %s\n", i, url[i]); // url[0] = a, url[1] = b
		i++;
	}

	printf("\nurl[%d] = %s\n", 0, url[0]); // url[0] = b ?
	printf("url[%d] = %s\n", 1, url[1]); // url[1] = b ?

	free(len);
	free(line);
	fclose(fp_url_list);
	return 0;
}

MfG
 
Zuletzt bearbeitet:
Diesen Code wird kein vernünftiger C-Compiler jemals kompilieren! Soll das nun eine Lösung in C oder C++ werden?
 
Stimmt - aber ein Compiler hat keine Vernunft und wird diesen Code deshalb kompilieren ;)

Code:
size_t *len = malloc(0);
Ich bin mir gar nicht mal sicher, ob ein malloc mit der Länge 0 überhaupt definiert ist - auf jeden Fall macht diese Zeile nichts sinnvolles.

Code:
char *line = (char *) malloc(sizeof(char));
Was soll diese Zeile - den Speicherplatz für ein einzelnes Zeichen bekommst du auch deutlich einfacher.

Code:
line_length = getline(&line, len, fp_url_list)
Das ist halt die Gefahr bei C - du schreibst hier eine Zeile in den Speicherplatz eines einzelnen Zeichens. Damit überschreibst du Speicherplatz, der eigentlich anderen Variablen gehört - damit ist die weitere Ausführung deines Programms undefiniert.

Code:
url[i] = line;
Das tut dann fast schon weh. Hier wird einfach der Zeiger auf deine Zeile (wie oben beschrieben ist das eigentlich nur ein einziges Zeichen) gespeichert - der Zeiger ist natürlich bei jedem Schleifendurchlauf identisch.
 
Simpson474 schrieb:
Stimmt - aber ein Compiler hat keine Vernunft und wird diesen Code deshalb kompilieren

Ein standardisierter C-Compiler wird diesen Code in zehntausend Jahren nicht kompileren, weil getline kein ANSI C ist. Diese Funktion existiert nur in ANSI C++ bzw. wird in der GNU C Extension definiert.
 
Ramm schrieb:
Getline ist kein ANSI-C aber mit dem GNU-gcc Compiler Funktioniert es. (Quelle oben)

Ich sagte bereits das getline nur in ANSI C++ bzw. in der GNU C Extension definiert ist.

Ich hasse diesen nicht Standarddreck! Dein Code wird von einem normalen C-Compiler nicht kompiliert werden.

Wenn du schon plattformspezifische Funktionen, wie getline verwenden willst, dann implementiere die GNU Funktion doch bitte in deinem Code, so dass jeder C-Compiler damit etwas anfangen kann.

Dein Code funktioniert natürlich so nicht. Mit der Zeile

Code:
url[i] = line;

weist du lediglich den Pointer auf einen festen Speicherbereich zu, nämlich auf den von getline allokierten line. Die Funktion getline überschreibt den String in diesem Speicher aber später mit dem nächsten String wieder. Deshalb siehst du nur b,b,b,b. Alle Zeiger zeigen auf ein und denselben Speicher.

Du wirst schon ein Array selbst anlegen müssen und den Speicherbereich dafür allokieren. Das getline kümmert sich nur um den Speicherbereich für die eingelesene Zeile und nicht um dein Array.

Also wirst du jetzt folgendes machen.

a) du fügst zu deinem Projekt die beiden Dateien.

getline.h

PHP:
/* getline.c -- Replacement for GNU C library function getline

Copyright (C) 1993 Free Software Foundation, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.  */

/* Written by Jan Brittenson, bson@gnu.ai.mit.edu.  */

/*
 * Modified for WinCvs/MacCVS : Alexandre Parenteau <aubonbeurre@hotmail.com> --- April 1998
 */

#ifndef _getline_h_
#define _getline_h_ 1

#include <stdio.h>

#ifdef __cplusplus
extern "C" {
#endif

#if defined (__GNUC__) || (defined (__STDC__) && __STDC__) || defined(__cplusplus)
#define __PROTO(args) args
#else
#define __PROTO(args) ()
#endif  /* GCC.  */

#ifndef __ssize_t_defined
#define __ssize_t int
#endif
__ssize_t
  getline __PROTO ((char **_lineptr, size_t *_n, FILE *_stream));
int
  getstr __PROTO ((char **_lineptr, size_t *_n, FILE *_stream,
		   char _terminator, int _offset));

#ifdef __cplusplus
}
#endif

#endif /* _getline_h_ */

getline.c

PHP:
/* getline.c -- Replacement for GNU C library function getline

Copyright (C) 1993 Free Software Foundation, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.  */

/* Written by Jan Brittenson, bson@gnu.ai.mit.edu.  */

/*
 * Modified for WinCvs/MacCVS : Alexandre Parenteau <aubonbeurre@hotmail.com> --- April 1998
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <sys/types.h>

#include <stdio.h>
#include <assert.h>
#include <errno.h>

#if STDC_HEADERS || defined(WIN32) || defined(TARGET_OS_MAC)
#include <stdlib.h>
#else
char *malloc (), *realloc ();
#endif

#include "getline.h"

/* Always add at least this many bytes when extending the buffer.  */
#define MIN_CHUNK 64

/* Read up to (and including) a TERMINATOR from STREAM into *LINEPTR
   + OFFSET (and null-terminate it). *LINEPTR is a pointer returned from
   malloc (or NULL), pointing to *N characters of space.  It is realloc'd
   as necessary.  Return the number of characters read (not including the
   null terminator), or -1 on error or EOF.  On a -1 return, the caller
   should check feof(), if not then errno has been set to indicate
   the error.  */

int
getstr (char **lineptr, size_t *n, FILE *stream, char terminator, int offset)
{
  int nchars_avail;		/* Allocated but unused chars in *LINEPTR.  */
  char *read_pos;		/* Where we're reading into *LINEPTR. */
  int ret;

  if (!lineptr || !n || !stream)
    {
      errno = EINVAL;
      return -1;
    }

  if (!*lineptr)
    {
      *n = MIN_CHUNK;
      *lineptr = (char *)malloc (*n);
      if (!*lineptr)
	{
	  errno = ENOMEM;
	  return -1;
	}
    }

  nchars_avail = *n - offset;
  read_pos = *lineptr + offset;

  for (;;)
    {
      int save_errno;
      register int c = getc (stream);

      save_errno = errno;

      /* We always want at least one char left in the buffer, since we
	 always (unless we get an error while reading the first char)
	 NUL-terminate the line buffer.  */

      assert((*lineptr + *n) == (read_pos + nchars_avail));
      if (nchars_avail < 2)
	{
	  if (*n > MIN_CHUNK)
	    *n *= 2;
	  else
	    *n += MIN_CHUNK;

	  nchars_avail = *n + *lineptr - read_pos;
	  *lineptr = (char *)realloc (*lineptr, *n);
	  if (!*lineptr)
	    {
	      errno = ENOMEM;
	      return -1;
	    }
	  read_pos = *n - nchars_avail + *lineptr;
	  assert((*lineptr + *n) == (read_pos + nchars_avail));
	}

      if (ferror (stream))
	{
	  /* Might like to return partial line, but there is no
	     place for us to store errno.  And we don't want to just
	     lose errno.  */
	  errno = save_errno;
	  return -1;
	}

      if (c == EOF)
	{
	  /* Return partial line, if any.  */
	  if (read_pos == *lineptr)
	    return -1;
	  else
	    break;
	}

#ifdef TARGET_OS_MAC
      if (terminator == '\n' && c == '\r')
	c = '\n';
#endif

      *read_pos++ = c;
      nchars_avail--;

      if (c == terminator)
	/* Return the line.  */
	break;
   }

  /* Done - NUL terminate and return the number of chars read.  */
  *read_pos = '\0';

  ret = read_pos - (*lineptr + offset);
  return ret;
}

__ssize_t
getline (char **lineptr, size_t *n, FILE *stream)
{
  return getstr (lineptr, n, stream, '\n', 0);
}

damit alle C-Compiler mit deinem Schmarrn etwas anfangen können.

b) Du änderst dein Programm folgendermaßen ab.

PHP:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "getline.h"

int main() 
{
    const size_t SIZE = 1024;
    FILE* fp_url_list;
    size_t len = 0;
    char* line = NULL;
    char** arr = (char**)malloc(sizeof(char*) * SIZE);
    size_t nRead, i, rows = 0;

    if( (fp_url_list = fopen("list.txt", "r")) == NULL) {
        fprintf(stderr, "\nKonnte Datei nicht öffnen!");
        return -1;
    }

    while(((nRead = getline(&line, &len, fp_url_list)) != EOF) && rows < SIZE) {
        arr[rows] = (char*)malloc(sizeof(char) * nRead + 1);
        strcpy(arr[rows], line);
        rows++;
    }

    for(i = 0; i < rows; i++)
        printf("arr[%d] = %s\n", i, arr[i]);

    for(i = 0; i < SIZE; i++)
        free(arr[i]);   // So wird free hier benutzt. Ansonsten gibts ein Speicherleck!
    free(arr);    // Zum Schluß noch den Top-Level Pointer freigeben.
    free(line);

    fclose(fp_url_list);

    return 0;
}

c) Alternativ dazu kannst du getline auch zwingen neuen Speicher zu allokieren, indem du den Zeiger wieder auf NULL setzst. Damit ersparst du dir das malloc und strcpy.

PHP:
    while(((nRead = getline(&line, &len, fp_url_list)) != EOF) && rows < SIZE) {
        arr[rows] = line;
        len = 0;
        line = NULL;
        rows++;
    }


    for(i = 0; i < rows; i++)
        printf("arr[%d] = %s\n", i, arr[i]);

    for(i = 0; i < SIZE; i++)
        free(arr[i]);
    free(arr);
    //free(line);    // Hier nicht explizit nochmal free aufrufen! Der Speicher wurde bereits in der Schleife freigegeben.

    fclose(fp_url_list);

    return 0;
 
Zuletzt bearbeitet:
Vielen Dank für deine Hilfe.

Die Verwendung von getline überdenke ich nochmals. Damit erspare ich mir wohl einige Probleme. Stattdessen kann man auch fgets verwenden.

Nochmals Danke.


MFG
 
Zurück
Oben