Forum: PC-Programmierung C Zeichenketten und Speicher


von toboe (Gast)


Lesenswert?

Hallo,

ich bin neu in der C-Programmierung, habe vorher nur in Java 
programmiert. Möchte gerne eine Datei zeilenweise auslesen und die 
Zeilen dann splitten. Mein bisheriger Code:
1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <string.h>
4
#include <assert.h>
5
6
7
char  *read_line(FILE *);
8
char **split    (const char *, const char *, int *);
9
10
11
int main(int argc, char *argv[])
12
{
13
  if (argc != 2) {
14
    printf("input file expected.\n");
15
    return EXIT_FAILURE;
16
  }
17
18
  FILE *input_file = fopen(argv[1], "r");
19
  if (NULL == input_file) {
20
    printf("couldn't open file %s.\n", argv[1]);
21
    return EXIT_FAILURE;
22
  }
23
24
  while (feof(input_file) == 0) {
25
    char *line = read_line(input_file);
26
    printf("%s\t%d\n", line, strlen(line));
27
28
    int num_tokens = 0;
29
    char **tokens = split(line, " ", &num_tokens);
30
31
    for (int i = 0; i < num_tokens; ++i) {
32
      printf("Token #%d: %s\t(%d)\n", (i + 1), tokens[i], strlen(tokens[i]));
33
      free(tokens[i]);
34
    }
35
36
    free(tokens);
37
    free(line);
38
  }
39
40
  fclose(input_file);
41
42
  return EXIT_SUCCESS;
43
}
44
45
46
char *read_line(FILE *input_file)
47
{
48
  assert (NULL != input_file);
49
50
  char *buffer = malloc(sizeof(char));
51
  assert (NULL != buffer);
52
53
  int length = 1;
54
  int index  = 0;
55
56
  int read = fgetc(input_file);
57
  while (EOF != read && '\n' != read) {
58
    if (index >= length) {
59
      length *= 2;
60
      buffer = realloc(buffer, length * sizeof(char) + 1);
61
      assert (NULL != buffer);
62
    }
63
    buffer[index] = read;
64
    ++index;
65
66
    read = fgetc(input_file);
67
  }
68
69
  buffer[index] = '\0';
70
  return buffer;
71
}
72
73
74
char **split(const char *str, const char *delimiters, int *num_tokens)
75
{
76
  assert (NULL != str);
77
  assert (NULL != delimiters);
78
  assert (NULL != num_tokens);
79
80
  char *string = malloc(strlen(str) + 1);
81
  assert (NULL != string);
82
  strcpy(string, str);
83
84
  char **tokens = malloc(sizeof(char *));
85
  assert (NULL != tokens);
86
87
  int number_tokens = 1;
88
  int index = 0;
89
90
  char *token = strtok(string, delimiters);
91
  while (NULL != token) {
92
    if (index >= number_tokens) {
93
      ++number_tokens;
94
      tokens = realloc(tokens, number_tokens * sizeof(char *));
95
      assert (NULL != tokens);
96
    }
97
    tokens[index] = malloc(strlen(token) + 1);
98
    assert (NULL != tokens[index]);
99
    strcpy(tokens[index], token);
100
101
    ++index;
102
    token = strtok(NULL, delimiters);
103
  }
104
105
  *num_tokens = number_tokens;
106
  return tokens;
107
}
Es scheint zu funktionieren, wie ich es mir vorgestellt hatte. 
Allerdings bin ich mir unsicher, ob ich den Speicher "richtig und 
komplett" freigegeben habe. Insbesondere habe ich Bedenken bei der 
split-Funktion, ob ich dort "string" noch freigeben muss. strtok 
verändert "string" ja und liefert Zeiger aus selbigen zurück. Reicht es 
dann, wenn ich das jeweilige aktuelle Token freigebe und nicht "string" 
als Ganzes am Ende der Funktion?

Und eine kleine Frage nebenbei: Für diese Kleinigkeit wirkt mir das 
Programm ziemlich "aufgebläht", obwohl ich von Java komme. Gibt es eine 
elegante Implementation für diese Funktionalität?

Ich bin für jede Antwort dankbar.

von Klaus W. (mfgkw)


Lesenswert?

toboe schrieb:
> strtok
> verändert "string" ja und liefert Zeiger aus selbigen zurück. Reicht es
> dann, wenn ich das jeweilige aktuelle Token freigebe und nicht "string"
> als Ganzes am Ende der Funktion?

Du darfast nur genau das freigeben, was du auch allokiert hast.

Also wenn du einen langen String allokierst und dann stückweise 
verarbeitest, musst du trotzdem genau den Stringanfang freigeben, das 
gilt dann für den gesamten Speicher wie er zuerst allokiert wurde.

Beim Versuch, stückweise freizugeben, was du in einem Rutsch allokiert 
hast, stürzt im besten Fall dein Programm ab.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.