-2

To my understanding, there is a way to parse input such that:

A million $ exit $$$ 16
The Cheit and its Punishment $$$ 8
War and Remembrance $$$ 12
Winds of War $$$ 12
How to Play Football $$$ 12
Ultrashort Pulses $$$ 8
Nonlinear Optics $$$ 8
etc..

Where the "$$$" separates between fields of data.
I'm looking to upgrade the phrase:

sscanf(line, " %200[^$][^$][^$]$$$%ld", name, &copies);

so it would fit line no. 1 in the example.

EDIT:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define NAME_LENGTH 200
#define ERROR -1
typedef int BOOL;
#define TRUE 1
#define FALSE 0

typedef struct book{
    char name[NAME_LENGTH];
    long copies;
    struct book *next;
} Book;

Book* create_book(char name[], long copies){
    Book *new_book = (Book*) malloc(sizeof(Book));
    if (new_book != NULL) {
        strcpy(new_book->name, name);
        new_book->next = NULL;
        new_book->copies = copies;
    }
    return new_book;
}

Book* add_first(Book *head, char name[], long copies){
    Book *new_book = create_book(name, copies);
    if (new_book == NULL)
        return NULL;
    new_book->next = head;
    return new_book;
}

Book* add_last(Book *head, char name[], long copies){
    Book *tail;
    Book *new_book = create_book(name, copies);
    if (new_book == NULL)
        return NULL;
    if (head == NULL)
        return new_book;
    tail = head;
    while (tail->next != NULL)
        tail = tail->next;
    tail->next = new_book;
    return head;
}

Book* add_sorted(Book *head, char name[], long copies){
    Book* iter, *prev = NULL;
    Book* new_book = create_book(name, copies);
    if(new_book == NULL)
        return head;
    if (head == NULL)
        return new_book;
    if (!strcmp(new_book->name, head->name)){
        new_book->next = head;
        return new_book;
    }
    iter = head;
    while ((iter != NULL) && (strcmp(new_book->name, head->name))){
        prev = iter;
        iter = iter->next;
    }
    prev->next = new_book;
    new_book->next = iter;
    return head;
}

int length(const Book *head){
    if (head == NULL)
        return 0;
    return 1 + length(head->next);
}

void free_library(Book *head_book){
    if (head_book == NULL)
        return;
    free_library(head_book->next);
    free(head_book);
}

Book* find_book(Book *head, char name[]){
    if (head == NULL)
        return NULL;
    if (strcmp(head->name, name) == 0)
        return head;
    find_book(head->next, name);
    return NULL;
}

Book* delete_book(Book *head, char name[]){
    Book *iter = head, *prev = NULL;
    if (head == NULL)
        return head;
    if ((!strcmp(head->name, name)) == 1){
        iter = head->next;
        free(head);
        return iter;
    }
    while (iter->next != NULL){
        if ((!strcmp(head->name, name)) == 1){
            prev->next = iter->next;
            free(iter);
            break;
        }
        prev = iter;
        iter = iter->next;
    }
    return head;
}

Book* initBooksList(FILE *input){
    Book *head_book = NULL, *existing_book = NULL;
    long copies = 0;
    char line[256] = {0}, name[NAME_LENGTH];
    if (input == NULL){
        printf("File did not open. Exit..\n");
        return NULL;
    }
    while(!feof(input)){
        if((fgets(line, 256, input) != NULL) && (head_book == NULL)){
            sscanf(line, " %200[^$][^$][^$]$$$%ld", name, &copies);
            printf("%s\n%ld\n", name, copies);
            head_book = create_book(name, copies);
            strcpy(line, "");
            strcpy(name, "");
            copies = 0;
        }
        else{
            sscanf(line, " %200[^$][^$][^$]$$$%ld", name, &copies);
            existing_book = find_book(head_book, name);
            if(existing_book != NULL){
                existing_book->copies += copies;
                printf("%s\n%ld\n", name, existing_book->copies);
            }
            else{
                add_sorted(head_book, name, copies);
                printf("%s\n%ld\n", name, copies);
                strcpy(line, "");
                strcpy(name, "");
                copies = 0;
            }
        }
    }
    return head_book;
}

void storeBooks(Book *head_book){

}

void returnBook(Book *head_book){

}

void borrowBook(Book *head_book){

}

int main(int argc, char *argv[]){
    int i = 0;
    FILE *ptr;
    printf("%d\n", argc);
    for(i = 0; i < argc; i++)
        printf("argv[%d] = %s\n", i, argv[i]);
    ptr = fopen(argv[1], "r");
    initBooksList(ptr);
    return 0;
}
  • 2
    The usual approach is to use `fgets` to read in the entire line into a suitable large buffer, then use `sscanf`/`strtok`/whatever to parse it. – Paul R Jun 15 '17 at 08:22
  • 2
    did you try ? Do you have a code to present? – Gam Jun 15 '17 at 08:22
  • 1
    Do you have a *maximum* line-length? And I'm not sure that `fscanf` (and family) might be the right choice here... Instead I would probably do something with `fgets` and `strstr`. – Some programmer dude Jun 15 '17 at 08:23
  • @Gem not just yet, I'm just collaborating with grater minds here :) – Ori Ashkenazi Jun 15 '17 at 08:26
  • @Someprogrammerdude the input is promised to be a non-hostile one. moreover, the first segment of every line may be of a maximum length of 200 characters (either a book's name or a command for the program to execute a function by- with the input after the command). – Ori Ashkenazi Jun 15 '17 at 08:28
  • @PaulR is there a way of using sscanf/strtok to parse a string by a specific delimiter? couldn't find anything too elegant as a solution... – Ori Ashkenazi Jun 15 '17 at 08:31
  • @OriAshkenazi Stack Overflow is __not__ a site for collaboration :) – Ajay Brahmakshatriya Jun 15 '17 at 08:34
  • @AjayBrahmakshatriya as stated on the about page of this site- "Developers trust Stack Overflow to help solve coding problems". my problem is that I'm not finding an elegant solution to a simple problem, if you don't have anything helpful to contribute i'd rather find it with the help of other people... – Ori Ashkenazi Jun 15 '17 at 08:39

2 Answers2

0

This should give you an idea what you could do:

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

int main()
{
  char line[] = "The Cheit and its Punishment $$$ 8";

  char *seppointer = strchr(line, '$');
  *seppointer = 0;    
  int price = atoi(seppointer + 4);

  printf("Title: %s\nPrice: %d\n", line, price);
}

Disclaimer: there is no error checking and it is assumed that the line has the required format.

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
0

If you know that the longest title is 200 characters, as indicated in your comment, you can allocate an array for this (including space for the null-terminator).

You can use fscanf() to parse the lines of your file with the format string " %200[^$]$$$%d". The first space tells fscanf() to skip over leading whitespaces, which may be left behind from previous I/O operations. The next conversion specifer is %200[^$], which tells fscanf() to read any characters into a string, until a $ is encountered. The $ is left in the input stream. Note that a maximum width of 200 is specified here to prevent buffer overflow. The next three characters in the format string, $$$, must be present in the input, and are matched before the final conversion specifier, %d, is reached.

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

#define MAX_TITLE  201

int main(void)
{
    /* Open file, and check for success */
    FILE *fp = fopen("data.txt", "r");
    if (fp == NULL) {
        perror("Unable to open file");
        exit(EXIT_FAILURE);
    }

    char title[MAX_TITLE];
    int price;

    while (fscanf(fp, " %200[^$]$$$%d", title, &price) == 2) {
        printf("Title: %s --- Price: $%d\n", title, price);
    }

    fclose(fp);

    return 0;
}

Here is the program output when run against your input file:

Title: The Cheit and its Punishment  --- Price: $8
Title: War and Remembrance  --- Price: $12
Title: Winds of War  --- Price: $12
Title: How to Play Football  --- Price: $12
Title: Ultrashort Pulses  --- Price: $8
Title: Nonlinear Optics  --- Price: $8

The calls to fscanf() in the above code leave the whitespace character following the last number on each line in the input stream; this is why the leading whitespace in the format string was needed. A better solution would be to use fgets() to fetch a line of input, and sscanf() to parse the line. A buffer should be allocated to hold the contents of each line as it is read; a generous allocation here is good, as it reduces the chance of long inputs leaving characters behind in the input stream. If there is a possibility of longer input, code should be added to clear the input stream before the next call to fgets().

One advantage to this approach is that, since an entire line is read including the \n, there is no need to skip leading whitespace characters as before. Another advantage is that spurious characters after the final number can be ignored, or handled by the code; since the line is stored it can be inspected and scanned as many times as needed. Characters following the final number would have caused problems for the first version, which was only equipped to skip leading whitespace.

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

#define BUF_SZ     1000
#define MAX_TITLE  201

int main(void)
{
    /* Open file, and check for success */
    FILE *fp = fopen("data.txt", "r");
    if (fp == NULL) {
        perror("Unable to open file");
        exit(EXIT_FAILURE);
    }

    char buffer[BUF_SZ];
    char title[MAX_TITLE];
    int price;
    size_t lnum = 0;

    while (fgets(buffer, BUF_SZ, fp) != NULL) {
        ++lnum;
        if (sscanf(buffer, "%200[^$]$$$%d", title, &price) == 2) {
            printf("Title: %s --- Price: $%d\n", title, price);
        } else {
            fprintf(stderr, "Format error in line %zu\n", lnum);
        }
    }

    fclose(fp);

    return 0;
}

The use of fgets() here allows more flexibility in inspecting the input. To handle cases where $ is part of the title, you can use strstr() to first find the delimiter " $$$", and then copy characters up to the delimiter into the title[] array in a loop. Since strstr() returns a pointer to the found string, this pointer can be given to sscanf() to pick out the final number. The strstr() function returns a null pointer if the string is not found, and this can be used to identify lines with formatting problems. Note that strstr() is in string.h:

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

#define BUF_SZ     1000
#define MAX_TITLE  201

int main(void)
{
    /* Open file, and check for success */
    FILE *fp = fopen("data.txt", "r");
    if (fp == NULL) {
        perror("Unable to open file");
        exit(EXIT_FAILURE);
    }

    char buffer[BUF_SZ];
    char title[MAX_TITLE];
    int copies;
    size_t lnum = 0;

    while (fgets(buffer, BUF_SZ, fp) != NULL) {
        ++lnum;

        /* Find delimiter string in buffer */
        char *title_end = strstr(buffer, " $$$");
        if (title_end == NULL) {
            fprintf(stderr, "Format error in line %zu\n", lnum);
            continue;
        } else {

            /* Copy characters into title until space before delimiter */
            char *curr = buffer;
            size_t i = 0;
            while (curr < title_end && i < MAX_TITLE) {
                title[i] = buffer[i];
                ++curr;
                ++i;
            }
            title[i] = '\0';
        }

        if (sscanf(title_end, " $$$%d", &copies) == 1) {
            printf("Title: %s --- Copies: %d\n", title, copies);
        } else {
            fprintf(stderr, "Format error in line %zu\n", lnum);
        }
    }

    fclose(fp);

    return 0;

}

Here is a modified input file:

The Cheit and its Punishment $$$ 8
War and Remembrance $$$ 12
Winds of War $$$ 12
A million $ exit $$$ 16
How to Play Football $$$ 12
Ultrashort Pulses $$$ 8
Nonlinear Optics $$$ 8

and the resulting output:

Title: The Cheit and its Punishment --- Copies: 8
Title: War and Remembrance --- Copies: 12
Title: Winds of War --- Copies: 12
Title: A million $ exit --- Copies: 16
Title: How to Play Football --- Copies: 12
Title: Ultrashort Pulses --- Copies: 8
Title: Nonlinear Optics --- Copies: 8
ad absurdum
  • 19,498
  • 5
  • 37
  • 60
  • best and most comprehensive explanation i had ever get. been trying to use fgets and strtok with no success so far. is using sscanf with the " %200[^$]$$$%d" input will work too? what should it look like? – Ori Ashkenazi Jun 15 '17 at 11:25
  • added a new "bug" the phrase should handle on the main question, have any ideas? – Ori Ashkenazi Jun 15 '17 at 13:49
  • @OriAshkenazi-- I was misunderstanding the problem you added in your last edit... answer updated. – ad absurdum Jun 15 '17 at 15:33