-1

I am a bit lost after hours of googling and research. I need to put a CSV containing artist, title, albumName, duration, yearRealeased, and hotttness. I then need to sort the array of structs by title alphabetically.

So far I am just able to get the title but then I get an error while running the compiled code (it prints out 100 or so of the 10000 titles) : "Segmentation fault (core dumped) makefile:5: recipe for target 'test' failed" make: *** [test] Error 139

The layout of the CSV file is: SongNumber, SongID, AlbumID, AlbumName, ArtistID, ArtistLatitude, ArtistLocation, ArtistLongitude, ArtistName, Danceability, Duration, KeySignature, KeySignatureConfidence, Tempo, Hotttnesss, TimeSignature, TimeSignatureConfidence, Title, Year

I do not know how to ignore the values I am not trying to read from the file. As I only need to get the artist, title, albumName, duration, yearRealeased, and hotttness.

Any help would be very appreciated as I am at a complete loss of what to do at this point. Thank You in advance.

I am also getting an error when compiling:

gcc -Wall -Wpedantic -std=c99 *.c -g -o 3240Assignment0
a0.c: In function ‘main’:
a0.c:52:34: warning: implicit declaration of function ‘strsep’ [-Wimplicit-function-declaration]
                  while ((Token = strsep(&line,","))) {
                                  ^
a0.c:52:32: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
                  while ((Token = strsep(&line,","))) {
                                ^
DarkSky42
  • 3
  • 2
  • Please show a minimal csv file that triggers the error. – Jabberwocky Jan 12 '18 at 15:49
  • The `strsep()` function is not standard C. Use `-std=gnu11` unless you really need to use C99 (or your compiler is so antique that it doesn’t know about C11, but then you should upgrade). To be effective, the `_BSD_SOURCE` macro must be defined before the first system header is included. – Jonathan Leffler Jan 12 '18 at 15:50
  • [How to debug small programs](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/) – user1810087 Jan 12 '18 at 15:52
  • Apparently `strsep` is not declared in ``, but strangely the program links. – Jabberwocky Jan 12 '18 at 15:53
  • @MichaelWalz: I think it is presented by gcc as an extension so it if probably exclude from string.h in `-std=c11` mode. But it is present in the library and it can link... – Serge Ballesta Jan 12 '18 at 15:55
  • @SergeBallesta yep, that explains it. – Jabberwocky Jan 12 '18 at 15:58
  • Are you using `strsep` because there is a possibility of *empty-fields* (e.g. `,,`) in your input file? Otherwise, you can tokenize with `strtok`. However, `strtok` cannot handle empty-fields (which is why we have `strsep`) – David C. Rankin Jan 12 '18 at 16:00
  • 1
    There are many issues, for example the usage of `strsep` is wrong; `sizeof(Token)` is not the length of the string pointed by `Token`, use `strlen(token) + 1` (+ 1 for the NUL terminator); `currentSong = (songPtr)(1 * sizeof(song));` is total nonsense and there are certainly a lot of other problems. – Jabberwocky Jan 12 '18 at 16:07
  • `typedef song *songPtr;` You will want to review: [Is it a good idea to **typedef** pointers?](http://stackoverflow.com/questions/750178/is-it-a-good-idea-to-typedef-pointers). – David C. Rankin Jan 12 '18 at 16:12
  • Are you sure that all your dynamic memory allocations are OK? allSongsArray may be very much wrong. Put it into debugger and see what you are getting. Segmentation fault is almost certainly result of a memory overwrite. See other similar questions as well: segmentation fault in during dynamic memory allocation with malloc – VladP Jan 12 '18 at 16:53
  • David there are empty fields and this is where I am running into issues as I am unsure of how to handle this and create a neat correct output. – DarkSky42 Jan 16 '18 at 17:16
  • Thank You Michael and VladP for your input and help. As you can imagine I am still learning and struggling to grasp some of the key concepts of coding in C. – DarkSky42 Jan 16 '18 at 17:17

2 Answers2

1

This is terrible:

 newSong.title = malloc(sizeof(Token));
 strcpy(newSong.title,Token);           // strcpy()

As Token is declared to be a char *, sizeof(Token) is sizeof(char *) (probably 4...). So your are likely to write past end of allocated data which invokes Undefined Behaviour.

What you need is:

newSong.title = strdup(Token);

if strict C11 compatibility is not required, or if it is:

 newSong.title = malloc(1 + strlen(Token));
 strcpy(newSong.title,Token);           // strcpy()

BTW, it you want C11 strict compatibility, you will need to use strtok instead of strsep, or revert to by hand separator search with strchr

Once this is fixed, try to process other fields the same you currently process title.

But beware, as you failed to give an example of file, I could not test and other problems could remain...

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • Thank You for your help. I am going to look at your suggestions and then come back with possible questions. – DarkSky42 Jan 16 '18 at 17:06
0

There are many issues and your program is overly complicated.

Here is the corrected and simplified version.

  • consistent indentation
  • No more typedef song *songPtr; which only adds confusion (read this for more details)
  • correct usage of strtok
  • replaced non standard strsep by the similar strtok (google "strtok vs strsep")
  • correct allocation of memory for songs
  • correct allocation of strings in songs (title, artist)
  • no more useless dynamic allocation of buffer which has a fixed size anyway
  • declaration of variables where they are used (this requires a non totally outdated C compiler)
  • avoiding useless (and in your case even wrong) copying around of data
  • etc.

There is still room for more improvement, for example there is no error checking for memory allocation.


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

#define BUFFSIZE 512
#define _BSD_SOURCE 
typedef struct song {
  char *artist;
  char *title;
  char *albumName;
  float duration;
  int yearRealeased;
  double hotttness;
} song;


int main() {
  FILE *songStream;
  int count = 0;
  int size = 100;

  song** allSongsArray = malloc(size * sizeof(song*));

  songStream = fopen("data.txt", "r");

  if (songStream == NULL) {
    printf("Unable to open file");
    exit(1);

  }
  else {
    printf("Opened File\n");

    char buffer[BUFFSIZE + 1];
    while (fgets(buffer, BUFFSIZE, songStream) != NULL) {

      song *newSong = malloc(sizeof(song));

        char *line = buffer;
      int index = 0;
      char *Token;

      while ((Token = strtok(line, ","))) {
        line = NULL;   // line needs to be set to NULL here, read 
                       // the strtok/strsep documentation for details

        switch (index)
        {
        case 17:
          newSong->title = malloc(strlen(Token) + 1);
          strcpy(newSong->title, Token);
          break;
        case 8:
          newSong->artist = malloc(strlen(Token) + 1);
          strcpy(newSong->artist, Token);
          break;

          // add the cases for the other columns here
        }

        index++;
      }

      allSongsArray[count++] = newSong;     // store pointer to new song in array

      if (count == size) {
        size = size + 100;
        allSongsArray = realloc(allSongsArray, size * sizeof(song*));
      }
    }

    fclose(songStream);
  }

  // display all songs read (count is the number of songs)
  for (int i = 0; i < count; i++)
  {
    fprintf(stdout, "%s, %s\n", allSongsArray[i]->title, allSongsArray[i]->artist);
  }

  return 0;
}

If strdup is available you can replace this:

newSong->artist = malloc(strlen(Token) + 1);
strcpy(newSong->artist, Token);

by:

newSong->artist = strdup(Token);

Or you can write your own strdup function (2 line sof code).

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
  • I really appropriate you taking the time to clarify some of my errors. I am going to be working on this further and hopefully generate a successful program. – DarkSky42 Jan 16 '18 at 17:08
  • When I use: allSongsArray = realloc(allSongsArray, size * sizeof(songPtr)); How do I still perform this function with deleting: typedef song *songPtr; – DarkSky42 Jan 16 '18 at 18:08
  • @DarkSky42 sorry the program I posted was not quite correct, it's corrected now. All `songPtr` have been replaced by `song*` and the `typedef` has been removed.. Read [this](https://stackoverflow.com/questions/750178/is-it-a-good-idea-to-typedef-pointers) for an explanation. – Jabberwocky Jan 17 '18 at 10:05
  • Thank you @Michael Waltz. Learning slowly so I appreciate the help tremendously. – DarkSky42 Jan 18 '18 at 14:42
  • @DarkSky42 if the answer is useful, please accept it and/or upvote it. – Jabberwocky Jan 18 '18 at 14:44
  • The problem I am running into is there are blank/NULL fields. Thus I have to use strsep(). However I can not seem to find and good examples of how to implement it. Any help would be appreciated. – DarkSky42 Jan 18 '18 at 15:18
  • If `strsep` is available on your platform, then use it. Otherwise ask another specific question. – Jabberwocky Jan 18 '18 at 15:32