for loop condition won't be checked properly - c

Im beginner to C/C++.I have defined a reverse strpbrk function which ignores parenthesis (that is named strrpbrk_outside) for a math parser project. Unfortunately program ignores the condition ch != bound.
Can anyone explain what the problem really is?
char* strrpbrk_outside(char* current,char* target,char* bound,char opening,char closing){
for(char* ch = current ; ch != bound ; --ch)
{
for(char* trg = target; *trg != '\0'; ++trg)
{
if ((*ch) == (*trg))
return ch;
}
if ((*ch) == closing)
{
ch = find_opening(ch,bound,opening,closing);
printf("current becomes %s\n",ch);
if (ch == bound)
printf("current == bound\n");
}
}
for(char* trg = target; *trg != '\0'; ++trg)
{
if ((*bound) == (*trg))
return bound;
}
return nullptr;
}
I've tested find_opening function and there wasn't any problem.
char* find_opening(char* current,char* bound,char opening,char closing){
int n = 0;
int i = 0;
if (*current == closing)
--current;//Caution:
for(; current != bound; --current)
{
if (*current == closing)
n = n + 1;
if (*current == opening)
{
i = i + 1;
if (i > n)
return current;
}
}
if (*bound == opening)
{
i = i + 1;
if (i > n)
return current;
}
return nullptr;//Caution: returns current and also checks the bound character
};
here is my test code:
#include <cstdio>
#include <string.h>
#include "AND_OR_NOT.h"
int main()
{
char str[100] = "!<white1>|<white2>&(<sites>|<words>)";
char str1[100] = "(!<white1>|<white2>)&(<sites>|<words>)";
char* pch1 = str1 + 20;
printf("%s\n",pch1-1);--pch1;
char* prev = strrpbrk_outside(pch1,"&|",str1,'(',')');
printf("%s\n",prev);
char END;
printf("press any key to end");
scanf("%c",&END);
}
and here is the strange result::
)&(<sites>|<words>)
current becomes (!<white1>|<white2>)&(<sites>|<words>)
current == bound
current becomes (_,
current becomes²"
current becomes (1'w
current becomes (
current becomes (µ"
current becomes (1'w
current becomes (1'w
current becomes (1'w
press any key to end
cmd image

Related

How to get whole structure from function?

This is my code:
#include <stdio.h>
typedef struct
{
char name[100];
char number[100];
} contact_t;
void empty_array(char *line)
{
for (int j = 0; line[j] != '\0'; j++)
{
line[j] = '\0';
}
}
void read_text(contact_t *contact)
{
int c, cnt = 0;
int i = 0;
char line[100];
do
{
c = getchar();
if ( (c == '\n') || (c == EOF))
{
if( cnt % 2 == 0)
{
for(int j = 0; line[j] != '\0'; j++)
contact -> name[j] = line[j];
}
else
{
for(int j = 0; line[j] != '\0'; j++)
contact -> number[j] = line[j];
}
empty_array(line);
i = 0;
cnt++;
}
line [i] = c;
i++;
} while (c != EOF);
}
int main()
{
contact_t contact = {"x", "0"};
int *j_ptr;
read_text(&contact);
printf("%s", contact.name);
printf("%s", contact.number);
return 0;
}
I am reading a text file(6 lines, name and number, name and number...) from standard input. Then I assign every second line(starting from the first) from that text file to structure contact.name and the rest are I assign to contact.number. So I have several 3 contact structures. I managed to pass to main only the last one, because I don't know how to get acces to int cnt and again make a for cycle.
This is what last prints give me:
John Green
254454556
UPDATE:
I am sorry for not being clear enough as I was writing this question in a hurry. This code is a part of school project and we are not allowed to work with dynamically allocated memory or use fscanf, fopen, qsort, lsearch, bsearch and hsearch etc. Basically, I would just like to use pointers to index of array line and then in main function use a for cycle again to pass all structures from the function read_text to main function of the program.
A few issues ...
main only provides space for one contact entry
read_text needs to use a dynamic array (vs. overwriting the same entry)
read_text needs to return the list pointer and the count to the caller (e.g. main)
The method used in read_text is a bit convoluted.
Style fixes:
contact -> name --> contact->name
list [i] --> list[i]
Here is the refactored code. It is annotated:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char name[100];
char number[100];
} contact_t;
int
read_text(contact_t **listp)
{
char buf[1000];
contact_t *list = NULL;
char *cp = NULL;
int cnt = 0;
// loop on input until EOF
while (fgets(buf,sizeof(buf),stdin) != NULL) {
// increase size of list
++cnt;
list = realloc(list,sizeof(*list) * cnt);
// handle error
if (list == NULL) {
perror("realloc/increase");
exit(1);
}
// point to current record
contact_t *contact = &list[cnt - 1];
// get first name
contact->name[0] = 0;
cp = strtok(buf," \n");
if (cp == NULL)
break;
strcat(contact->name,cp);
// add separater
strcat(contact->name," ");
// get last name
cp = strtok(NULL," \n");
if (cp == NULL)
break;
strcat(contact->name,cp);
// get number
cp = strtok(NULL," \n");
if (cp == NULL)
break;
strcpy(contact->number,cp);
}
// trim to actual amount stored (if error)
if ((cp == NULL) && (cnt > 0)) {
--cnt;
list = realloc(list,sizeof(*list) * cnt);
if (list == NULL) {
perror("realloc/trim");
exit(1);
}
}
// give caller the list pointer
*listp = list;
return cnt;
}
int
main(void)
{
int cnt;
contact_t *list;
cnt = read_text(&list);
// print all entries read in
for (int idx = 0; idx < cnt; ++idx) {
contact_t *contact = &list[idx];
printf("'%s' '%s'\n",contact->name,contact->number);
}
return 0;
}
Here is the test input I used:
John Green 254454556
Fred Smith 8765309
Bob Jones 99728967341
Mary Gallagher 4329268757
Here is the program output:
'John Green' '254454556'
'Fred Smith' '8765309'
'Bob Jones' '99728967341'
'Mary Gallagher' '4329268757'
UPDATE:
I am sorry, I should have clarified that I cannot use dynamically allocated memory. Malloc, calloc or also fsangf is not available –
gregalz
Okay, no malloc et. al. Ironically, I was going to use a predefined fixed size array. But, decided to use a dynamic array instead ;-)
Not sure what fsangf is. So, I'll assume that's fscanf. If you're heavily restricted, maybe you should edit your question and post what you can and can not use.
Here's the code that uses just a fixed array:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char name[100];
char number[100];
} contact_t;
#define NLIST 1000
contact_t list[NLIST];
int
read_text(contact_t *list,int max)
{
char buf[1000];
char *cp = NULL;
int cnt = 0;
// loop on input until EOF
while (fgets(buf,sizeof(buf),stdin) != NULL) {
// don't overflow the max size
if (cnt >= max)
break;
// point to current record and increase list count
contact_t *contact = &list[cnt++];
// get first name
contact->name[0] = 0;
cp = strtok(buf," \n");
if (cp == NULL)
break;
strcat(contact->name,cp);
// add separater
strcat(contact->name," ");
// get last name
cp = strtok(NULL," \n");
if (cp == NULL)
break;
strcat(contact->name,cp);
// get number
cp = strtok(NULL," \n");
if (cp == NULL)
break;
strcpy(contact->number,cp);
}
// trim to actual amount stored (if error)
if ((cp == NULL) && (cnt > 0))
--cnt;
return cnt;
}
int
main(void)
{
int cnt;
cnt = read_text(list,NLIST);
// print all entries read in
for (int idx = 0; idx < cnt; ++idx) {
contact_t *contact = &list[idx];
printf("'%s' '%s'\n",contact->name,contact->number);
}
return 0;
}

Memory leak caused by realloc in valgrind C

I have a my_string object made that contains a char pointer, a size, and a capacity. I am trying to read words from a dictionary text file into the my_string object and print them to the screen. The default capacity is 7, so when I read a word that is longer than 7 characters, I need to reallocate some space at the end of the string. I haven't been able to implement this functionality correctly yet. It seems to work correctly, but when I run it in valgrind, there is apparently a memory leak somewhere in my code. Not sure where this is coming from, because I free the entire string after the program runs. If someone could help me with this you'd be a lifesaver. Here is my driver code, and my my_string.c file:
main.c
#include <stdio.h>
#include <stdlib.h>
#include "my_string.h"
int main(int argc, char* argv[]) {
MY_STRING hMy_String = NULL;
FILE* fp;
hMy_String = my_string_init_default();
fp = fopen("dictionary.txt", "r");
int len;
while(my_string_extraction(hMy_String, fp)) {
len = my_string_get_size(hMy_String);
if(len == 8){
my_string_insertion(hMy_String, stdout);
printf("\n");
if(fgetc(fp) == ' '){
printf("Found a space after the string\n");
}
}
}
my_string_destroy(&hMy_String);
fclose(fp);
return 0;
}
my_string.c
#include <stdio.h>
#include <stdlib.h>
#include "my_string.h"
struct my_string {
int size;
int capacity;
char* data;
};
typedef struct my_string My_String;
MY_STRING my_string_init_default(void){
//default capacity of string is 7
//initializes pointer to My_String, set to NULL for good practice
My_String* pMy_String = NULL;
//allocates memory for default string
pMy_String = (My_String*)malloc(sizeof(My_String));
if(pMy_String != NULL) {
pMy_String->size = 0;
pMy_String->capacity = 7;
pMy_String->data = (char*)malloc(sizeof(char) * pMy_String->capacity);
if(pMy_String->data == NULL) {
free(pMy_String);
pMy_String = NULL;
}
}
//returns copy of address to default string
return pMy_String;
}
MY_STRING my_string_init_c_string(char* c_string) {
int i = 0;
//initializes a pointer to a My_String
My_String* theString = NULL;
//loops through string, for every character, i increases 1
while((*c_string) != '\0'){
c_string++;
i++;
}
//resets c_strings value from before the loop
c_string = c_string - i;
i++; //i needs to be 1 greater than the length of the string
//allocation
theString = malloc(sizeof(My_String) + i);
//if there was an error, return NULL
if(theString == NULL)
return NULL;
else {
//sets the values of the object to the given values
(*theString).size = (i - 1);
(*theString).capacity = i;
(*theString).data = c_string;
}
//returns address of the initialized string
return theString;
}
int my_string_get_capacity(MY_STRING hMy_string){
return sizeof(hMy_string);
}
int my_string_get_size(MY_STRING hMy_string) {
char* str = (char*) hMy_string;
int size = 0;
while(str[size] != '\0')
size++;
return size;
}
int my_string_compare(MY_STRING hLeft_string, MY_STRING hRight_string) {
// sets the strings to pointers to the My_String data type
My_String* Left = hLeft_string;
My_String* Right = hRight_string;
//variables for lexicographical value of each string
int left_lex = 0;
int right_lex = 0;
//loops through left string, adds up left_lex value
while((*Left->data) != '\0') {
left_lex += (*Left->data);
(*Left).data++;
}
//loops through right string, adds up right_lex value
while((*Right->data) != '\0') {
right_lex += (*Right->data);
(*Right).data++;
}
//does the comparison and returns the corrosponding value
if(left_lex < right_lex){
return -1;
}else if (left_lex == right_lex) {
return 0;
}else {
return 1;
}
}
Status my_string_extraction(MY_STRING hMy_string, FILE* fp) {
My_String* pMy_string = hMy_string;
int start = 0, end = 0;
char ch;
int strLength, capacity, i;
while(!feof(fp)){
ch = fgetc(fp);
if(ch == ' ' || ch == '\t' || ch == '\n' ||ch == '\r') {
continue;
}
else {
start = ftell(fp) - 1;
end = start + 1;
break;
}
}
if(end == 0){
return FAILURE;
}
while(!feof(fp)) {
ch = fgetc(fp);
end++;
if(ch == ' ' || ch == '\t' || ch == '\n' ||ch == '\r'){
break;
}
}
strLength = end - start - 1;
(*pMy_string).size = strLength;
if(strLength == 0){
return FAILURE;
}
capacity = my_string_get_capacity(hMy_string);
if(strLength >= capacity){
hMy_string = realloc(hMy_string, strLength + 1);
}
fseek(fp, start, SEEK_SET);
char* str = (char*)hMy_string;
for(i = 0; i < strLength; i++) {
str[i] = fgetc(fp);
}
str[i] = '\0';
return SUCCESS;
}
Status my_string_insertion(MY_STRING hMy_string, FILE* fp) {
char* str = (char*)hMy_string;
if(fprintf(fp, "%s", str))
return SUCCESS;
else
return FAILURE;
}
void my_string_destroy(MY_STRING* phMy_string){
free(*phMy_string);
*phMy_string = NULL;
}

(char*)malloc(sizeof(char)) causing segmentation fault, how?

The code works fine on most inputs, but for userID's whih are very long I get a segmentation fault. My question is, how can malloc cause a segmentation fault? simply allocating memory shouldn't cause this. I found the problem area by using printf() statements, it seem the malloc within my read_line() function is where the problem is because the second "read_line" does not print, but the first before the malloc does.
thank you.
- Chris
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#define DELIM " " /* the delimiter */
#define MAX_CHANGE (10.0/86400.0) /* 10kg/day */
/* seconds in a day is 24 hours * 60 minutes * 60 seconds */
#define MEM_OUT printf("%s","out of memory");
/* Defines Node structure. */
struct Node{
char *id;
float weight;
int time;
struct Node *next;
} *head, *p, *t, *last;
/* Constructor which returns a pointer to a new node. */
struct Node * new_node(int time, char *id, float w)
{ /*note malloc returns a pointer */
struct Node *node = (struct Node *)malloc(sizeof(struct Node));
node->time = time;
node->id = (char *)malloc( (strlen(id) + 1) * sizeof(char));
strcpy(node->id, id); //duplicate the id, so new node has own copy.
node->weight = w;
node->next = NULL;
return node;
}
/* reads in line of characters until either a EOF or '\n' is encountered
then places a the terminator '\0' at the end */
char * read_line(FILE *stream)
{
printf("read_line");
char * temp = (char*)malloc(sizeof(char));
printf("read_line");
char * line = (char*)malloc(sizeof(char));
char c;
*line = '\0';
int i = 1;
//strchr()
while( (c = getc(stream)) != EOF && c != '\n')
{
//if(c == EOF) return NULL;
//realloc(line,++i);
strcpy(temp,line);
line = malloc(++i * sizeof(char));
strcpy(line,temp);
temp = malloc(i * sizeof(char));
*(line + (i-1)) = '\0';
*(line + (i-2)) = c;
}
free(temp);
if( i == 1) return NULL;
return line;
}
main() {
int lasttime = 0, timestamp, duration, tokens;
char * userID = NULL;
char * lastuserID = NULL;
char * line = NULL;
float weight,lastweight,change,changePerTime;
head = new_node(0,"",0.0);
last = head;
FILE *fp = fopen("C:\\Users\\chris\\Desktop\\School\\York\\cse\\2031 Software Tools\\Labs\\6\\input.txt","r");
while( (line = read_line(fp)) != '\0') {
printf("%s\n",line);
//free(userID);
line = strtok(line, " \n");
if (line == NULL || sscanf(line,"%d",&timestamp) < 1 || timestamp == 0){
printf("%s\n","Invalid time");
continue;
}
line = strtok(NULL, " \n");
if(line == NULL || isdigit(line[0]) || line[0] == '.') {
printf("Illegal userID");
//free(line);
continue;
}
userID = (char * )malloc( (strlen(line)+1) * sizeof(char));
strcpy(userID,line);
strcat(userID," ");
do{
line = strtok(NULL," \n");
if(line != NULL && !isdigit(line[0]) && line[0] != '.'){
strcat(userID,line ); // adds ' ' and '\0'
strcat(userID," ");
}
}while(line != NULL && line[0] != '.' && !isdigit(line[0]) );
userID[strlen(userID)-1] = '\0'; //erases the tailing space.
if(strlen(userID) > 179){
printf("Illegal userID\n");
printf("mid");
continue;
printf("%s\n","after" );
}
if(line != NULL)
tokens = sscanf(line,"%f", &weight);
if(line == NULL || tokens < 1 || weight < 30.0 || weight > 300.0)
{printf("Illegal weight\n"); continue; }
if (lasttime >= timestamp){
printf("Nonmonotonic timestamps\n");
continue;
}
lasttime = timestamp;
// record is valid apst this point.
/* t = last occurence of this userID, p = last userID*/
for(p = head, t = NULL; p != NULL; p = p->next)
{
if(strcmp(userID,p->id) == 0)
t=p;
last = p; // set last to last p.
}
if(t == NULL)
printf("OK newuser\n");
else if(t != NULL)
{
duration = timestamp - t->time;
change = weight - t->weight;
changePerTime = change / duration;
if(changePerTime < -MAX_CHANGE || changePerTime > MAX_CHANGE)
printf("Suspiciously large weight change\n");
else
printf("OK\n");
}
/* add new node to end of list */
last->next = new_node(timestamp,userID,weight);
/* update lastnode */
last = last->next;
free(line);
}
fclose(fp);
/* count sum of id's for last valid user*/
int count=0;
for(p = head->next; p !=NULL; p=p->next)
{
if(strcmp(last->id,p->id) == 0)
count++;
}
//fclose(f); // use if input from file is uncommented
// adds 1 to both demensions to hole axis
int tHeight = 11;
int tWidth = count + 1;
int qHeight = 10;
int qWidth= count;
/* builds the barchart */
char bc[tHeight][tWidth]; // + 1 for y-axis
/* draws axis and orgin */
int a,b;
for(a=0; a<tHeight; a++)
{
for(b=0;b<tWidth; b++)
{
if(a == qHeight && b == 0)
bc[a][b] = '+';
else if(a < tHeight && b == 0)
bc[a][b] = '|';
else if(a == qHeight && b > 0)
bc[a][b] = '-';
}
}
/* prints the bars */
int j=1, i, k, bh;
for(p = head; p != NULL, j < tWidth; p=p->next)
{
if(strcmp(last->id,p->id) == 0)
{
for(i = 9, k=0, bh = (int)(p->weight / 30);i >= 0; i--)
{
if(k < bh)
{
bc[i][j] = '*';
k++; // barheight
}
else
bc[i][j] = ' ';
}
j++;
}
}
/* prints the barchart */
int m, n;
for(m=0; m < tHeight; m++)
{
for(n=0; n < tWidth; n++)
{
printf("%c",bc[m][n]);
}
printf("%c",'\n');
}
}
The malloc calls are not causing a segmentation fault. But your use of them later on could be.
Some Items of Note
Your printf("read line") statements will not print out immediately when called because the output is buffered. If you want them to print right away, do printf("read line\n"). You'll then see that both execute and your code that uses the tiny buffer you allocated will cause the crash.
In your while loop, you are doing more malloc calls and assigning the returns to variables, like temp and line, without freeing the prior memory pointers that temp and line hold, thus causing some memory leaks. Your commented out realloc was the better thought process: line = realloc(line, ++i * sizeof(*line));. Similarly for temp.
Memory Allocation Problem
One very problematic area is here:
userID = (char * )malloc( (strlen(line)+1) * sizeof(char));
strcpy(userID,line);
strcat(userID," ");
userID can hold the length of the string in line (strlen(line)) plus one more byte. But that one more byte is needed for the null terminator. Your strcat(userID, " ") will write past the length of the allocated buffer for userID by one byte.
I solved the problem! The issue wasn't the read_line function at all, it was the memory allocation for the userID string. Moving the malloc() for the user to the beginning of the loop fixed the problem.
The amount of memory allocated for the userID portion of the line was based on the the length of the whole line. eg: malloc(strlen(line)+2). However this was done after calling strtok() on the line a few times, which would allocate a memory block shorter than the length of the entire line. This is because strtok() places null terminators '\0' 's at every instance of the specified delimiter in the line, and strlen() only counts the length from the passed character pointer to the first '\0' it encounters.
Anyway, thanks for your help guys!
-Chris
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#define DELIM " " /* the delimiter */
#define MAX_CHANGE (10.0/86400.0) /* 10kg/day */
/* seconds in a day is 24 hours * 60 minutes * 60 seconds */
/* Defines Node structure. */
struct Node{
char *id;
float weight;
int time;
struct Node *next;
} *head, *p, *t, *last;
/* Constructor which returns a pointer to a new node. */
struct Node * new_node(int time, char *id, float w)
{ /*note malloc returns a pointer */
struct Node *node = (struct Node *)malloc(sizeof(struct Node));
node->time = time;
node->id = malloc(strlen(id) + 1);
strcpy(node->id, id); //duplicate the id, so new node has own copy.
node->weight = w;
node->next = NULL;
return node;
}
/* reads in line of characters until either a EOF or '\n' is encountered
then places a the terminator '\0' at the end */
char * read_line(FILE *in)
{
int i = 1;
char * s = NULL;
char c;
do{
s = realloc(s,i); //strlen does not work on NULL strings
if(s == NULL || s == "")
{
printf("%s\n","out of memory");
exit(1);
}
*(s + (i-1)) = '\0'; // ensures null terminated
if(i > 1)
*(s + (i-2)) = c;
i++;
}
while( (c = getc(in)) != EOF && c != '\n' );
if (c == '\n')
return s;
else if(c == EOF)
return NULL;
}
main() {
int lasttime = 0, timestamp, duration, tokens;
char * userID = NULL;
char * lastuserID = NULL;
char * line = NULL;
float weight,lastweight,change,changePerTime;
head = new_node(0,"",0.0);
last = head;
FILE *fp = fopen("C:\\Users\\chris\\Desktop\\School\\York\\cse\\2031 Software Tools\\Labs\\6\\tests\\04.in","r");
while((line = read_line(fp)) != NULL) {
userID = malloc(strlen(line)+2); // max userID length is line length**
line = strtok(line, " \n");
if (line == NULL || sscanf(line,"%d",&timestamp) < 1 || timestamp == 0){
printf("%s\n","Invalid time");
continue;
}
line = strtok(NULL, " \n");
if(line == NULL || isdigit(line[0]) || line[0] == '.') {
printf("%s\n","Illegal userID");
//free(line);
continue;
}
strcpy(userID,line);
strcat(userID," ");
do{
line = strtok(NULL," \n");
if(line != NULL && !isdigit(line[0]) && line[0] != '.'){
strcat(userID,line ); // adds ' ' and '\0'
strcat(userID," ");
}
}while(line != NULL && line[0] != '.' && !isdigit(line[0]) );
userID[strlen(userID)-1] = '\0'; //erases the tailing space.
if(strlen(userID) > 179){
printf("Illegal userID\n");
free(userID);
free(line);
continue;
}
if(line != NULL)
tokens = sscanf(line,"%f", &weight);
if(line == NULL || tokens < 1 || weight < 30.0 || weight > 300.0)
{printf("Illegal weight\n"); continue; }
if (lasttime >= timestamp){
printf("Nonmonotonic timestamps\n");
continue;
}
lasttime = timestamp;
// record is valid apst this point.
/* t = last occurence of this userID, p = last userID*/
for(p = head, t = NULL; p != NULL; p = p->next)
{
if(strcmp(userID,p->id) == 0)
t=p;
last = p; // set last to last p.
}
if(t == NULL)
printf("OK newuser\n");
else if(t != NULL)
{
duration = timestamp - t->time;
change = weight - t->weight;
changePerTime = change / duration;
if(changePerTime < -MAX_CHANGE || changePerTime > MAX_CHANGE)
printf("Suspiciously large weight change\n");
else
printf("OK\n");
}
/* add new node to end of list */
last->next = new_node(timestamp,userID,weight);
/* update lastnode */
last = last->next;
free(line);
} // end of input loop
fclose(fp);
/* count sum of id's for last valid user*/
int count=0;
for(p = head->next; p !=NULL; p=p->next)
{
if(strcmp(last->id,p->id) == 0)
count++;
}
//fclose(f); // use if input from file is uncommented
// adds 1 to both demensions to hole axis
int tHeight = 11;
int tWidth = count + 1;
int qHeight = 10;
int qWidth= count;
/* builds the barchart */
char bc[tHeight][tWidth]; // + 1 for y-axis
/* draws axis and orgin */
int a,b;
for(a=0; a<tHeight; a++)
{
for(b=0;b<tWidth; b++)
{
if(a == qHeight && b == 0)
bc[a][b] = '+';
else if(a < tHeight && b == 0)
bc[a][b] = '|';
else if(a == qHeight && b > 0)
bc[a][b] = '-';
}
}
/* prints the bars */
int j=1, i, k, bh;
for(p = head; p != NULL, j < tWidth; p=p->next)
{
if(strcmp(last->id,p->id) == 0)
{
for(i = 9, k=0, bh = (int)(p->weight / 30);i >= 0; i--)
{
if(k < bh)
{
bc[i][j] = '*';
k++; // barheight
}
else
bc[i][j] = ' ';
}
j++;
}
}
/* prints the barchart */
int m, n;
for(m=0; m < tHeight; m++)
{
for(n=0; n < tWidth; n++)
{
printf("%c",bc[m][n]);
}
printf("%c",'\n');
}
}

Tokenize String by using pointer

I'm trying to tokenize a sting and here is my attempt.
char new_str[1024];
void tokenize_init(const char str[]){//copy the string into global section
strcpy(new_str,str);
}
int i = 0;
char *tokenize_next() {
const int len = strlen(new_str);
for(; i <= len; i++) {
if ( i == len) {
return NULL;
}
if ((new_str[i] >= 'a' && new_str[i] <= 'z') ||
(new_str[i] >= 'A' && new_str[i] <= 'Z')) {
continue;
}else {
new_str[i] = '\0';
i = i + 1;
return new_str;
}
}
return NULL;
}
//main function
int main(void) {
char sentence[] = "This is a good-sentence for_testing 1 neat function.";
printf("%s\n", sentence);
tokenize_init(sentence);
for (char *nt = tokenize_next();
nt != NULL;
nt = tokenize_next())
printf("%s\n",nt);
}
However, it just print out the first word of the sentence(which is "This") and then stop. Can someone tell me why? My guess is my new_str is not persisent and when the main function recall tokenize_next() the new_str become just the first word of the sentence. Thanks in advance.
The reason that it only prints out "This" is because you iterate to the first non-letter character which happens to be a space, and you replace this with a null terminating character at this line:
new_str[i] = '\0';
After that, it doesn't matter what you do to the rest of the string, it will only print up to that point. The next time tokenize_next is called the length of the string is no longer what you think it is because it is only counting the word "This" and since "i" has already reached that amount the function returns and so does every successive call to it:
if ( i == len)
{
return NULL;
}
To fix the function you would need to somehow update your pointer to look past that character on the next iteration.
However, this is quite kludgy. You are much better off using one of the mentioned functions such as strtok or strsep
UPDATE:
If you cannot use those functions then a redesign of your function would be ideal, however, per your request, try the following modifications:
#include <string.h>
#include <cstdio>
char new_str[1024];
char* str_accessor;
void tokenize_init(const char str[]){//copy the string into global section
strcpy(new_str,str);
str_accessor = new_str;
}
int i = 0;
char* tokenize_next(void) {
const int len = strlen(str_accessor);
for(i = 0; i <= len; i++) {
if ( i == len) {
return NULL;
}
if ((str_accessor[i] >= 'a' && str_accessor[i] <= 'z') ||
(str_accessor[i] >= 'A' && str_accessor[i] <= 'Z')) {
continue;
}
else {
str_accessor[i] = '\0';
char* output = str_accessor;
str_accessor = str_accessor + i + 1;
if (strlen(output) <= 0)
{
str_accessor++;
continue;
}
return output;
}
}
return NULL;
}
//main function
int main(void) {
char sentence[] = "This is a good-sentence for_testing 1 neater function.";
printf("%s\n", sentence);
tokenize_init(sentence);
for (char *nt = tokenize_next(); nt != NULL; nt = tokenize_next())
printf("%s\n",nt);
}

String Search and format in C

Just a quick one: in C I have a buffer full of data like below:
char buffer[255]="CODE=12345-MODE-12453-CODE1-12355"
My question is how to search through this. For example for the CODE=12345, section bear in mind that the numbers change, so I would like to search like this CODE=***** using wildcard or preset amount of spaces after the CODE= part.
This method wont compile last one left to try
#include <stdio.h>
#include <string.h>
#include <windows.h>
int main ()
{
char buf[255]="CODE=12345-MODE-12453-CODE1-12355";
#define TRIMSPACES(p) while(*p != '\0' && isspace((unsigned char)*p) != 0) ++p
#define NSTRIP(p, n) p += n
#define STRIP(p) ++p
char* getcode(const char *input)
{
char *p = (char*) input, *buf, *pbuf;
if((buf = malloc(256)) == NULL)
return NULL;
pbuf = buf;
while(*p != '\0') {
if(strncmp(p, "CODE", 3) == 0) {
NSTRIP(p, 4); //remove 'code'
TRIMSPACES(p);//trim white-space after 'code'
if(*p != '=')
return NULL;
STRIP(p); // remove '='
TRIMSPACES(p); //trim white-spaces after '='
/* copy the value until found a '-'
note: you must be control the size of it,
for avoid overflow. we allocated size, that's 256
or do subsequent calls to realloc()
*/
while(*p != '\0' && *p != '-')
*pbuf ++ = *p++;
// break;
}
p ++;
}
//put 0-terminator.
*pbuf ++ = '\0';
return buf;
}
//
}
You could use the sscanf() function:
int number;
sscanf(buffer, "CODE = %i", &number);
for that to work well your buffer has to be null terminated.
Another way to do it instead of sscanf():
char *input, *code;
input = strstr(buf, "CODE");
if(input == NULL) {
printf("Not found CODE=\n");
return -1;
}
code = strtok(strdup(input), "=");
if(code != NULL) {
code = strtok(NULL, "-");
printf("%s\n", code); // code = atoi(code);
} else {
//not found '='
}
Or more robust way.. a bit more complex:
#define TRIMSPACES(p) while(*p != '\0' && isspace((unsigned char)*p) != 0) ++p
#define NSTRIP(p, n) p += n
#define STRIP(p) ++p
char* getcode(const char *input, size_t limit)
{
char *p = (char*) input, *buf, *pbuf;
size_t i = 0;
while(*p != '\0') {
if(strncmp(p, "CODE", 3) == 0) {
NSTRIP(p, 4); //remove 'code'
TRIMSPACES(p);//trim all white-spaces after 'code'
/* check we have a '=' after CODE (without spaces).
if there is not, returns NULL
*/
if(*p != '=')
return NULL;
/* ok. We have.. now we don't need of it
just remove it from we output string.
*/
STRIP(p);
/* remove again all white-spaces after '=' */
TRIMSPACES(p);
/* the rest of string is not valid,
because are white-spaces values.
*/
if(*p == '\0')
return NULL;
/* allocate space for store the value
between code= and -.
this limit is set into second parameter.
*/
if((buf = malloc(limit)) == NULL)
return NULL;
/* copy the value until found a '-'
note: you must be control the size of it,
for don't overflow. we allocated 256 bytes.
if the string is greater it, do implementation with
subjecents call to realloc()
*/
pbuf = buf;
while(*p != '\0' && *p != '-' && i < limit) {
*pbuf ++ = *p++;
i ++;
}
*pbuf ++ = '\0';
return buf;
}
p ++;
}
return NULL;
}
And then:
char buf[255] = "foo baa CODE = 12345-MODE-12453-CODE-12355";
char *code = getcode(buf,256);
if(code != NULL) {
printf("code = %s\n", code);
free(code);
} else {
printf("Not found code.\n");
}
output:
code = 12345
Check out this online.
if you want to don't differentiate case, you can use the strncasecmp() that's POSIX function.
Assuming the CODE= part always comes at the beginning of the string, it's pretty easy:
sscanf(buffer, "CODE = %d", &number);
...but you want buffer to be char[255], not unsigned long.
Edit: If the CODE= part isn't necessarily at the beginning of the string, you can use strstr to find CODE in the buffer, do your sscanf starting from that point, then look immediately following that:
int codes[256];
char *pos = buffer;
size_t current = 0;
while ((pos=strstr(pos, "CODE")) != NULL) {
if (sscanf(pos, "CODE = %d", codes+current))
++current;
pos += 4;
}
Edit2:
For example, you'd use this something like this:
#include <stdio.h>
#include <string.h>
#include <windows.h>
int main ()
{
// This is full of other junk as well
char buffer[255]="CODE=12345 MODE-12453 CODE=12355" ;
int i;
int codes[256];
char *pos = buffer;
size_t current = 0;
while ((pos=strstr(pos, "CODE")) != NULL) {
if (sscanf(pos, "CODE = %d", codes+current))
++current;
pos += 4;
}
for (i=0; i<current; i++)
printf("%d\n", codes[i]);
return 0;
}
For me, this produces the following output:
12345
12355
...correctly reading the two "CODE=xxx" sections, but skipings over the "MODE=yyy" section.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *getcode(const char *str, const char *pattern){
//pattern: char is match, space is skip, * is collect
static const char *p=NULL;
char *retbuf, *pat;
int i, match, skip, patlen;
if(str != NULL) p=str;
if(p==NULL || *p=='\0') return NULL;
if(NULL==(retbuf=(char*)malloc((strlen(p)+1)*sizeof(char))))
return NULL;
pat = (char*)pattern;
patlen = strlen(pat);
i = match = skip = 0;
while(*p){
if(isspace(*p)){
++p;
++skip;
continue;
}
if(*pat){
if(*p == *pat){
++match;
++p;
++pat;
} else if(*pat == '*'){
++match;
retbuf[i++]=*p++;
++pat;
} else {
if(match){//reset
pat=(char*)pattern;
p -= match + skip -1;
i = match = skip = 0;
} else //next
++p;
}
} else {
break;
}
}
if(i){//has match
retbuf[i++]='\0';
retbuf=realloc(retbuf, i);
return retbuf;
} else {
free(retbuf);
return NULL;
}
}
int main (){
char *code;
code=getcode("CODE=12345-MODE-12453-CODE1-12355", "CODE=*****");
printf("\"%s\"\n",code);//"12345"
free(code);
code=getcode(" CODE = 12345-MODE-12453-CODE1-12355", "CODE=*****");
printf("\"%s\"\n",code);//"12345"
free(code);
code=getcode("CODE-12345-MODE-12453-CODE1-12355", "CODE=*****");
if(code==NULL)printf("not match\n");//not match
code=getcode("CODE=12345-MODE-12453-CODE=12355", "CODE=*****");
printf("\"%s\"\n",code);//"12345"
free(code);
code=getcode(NULL, "CODE=*****");
printf("\"%s\"\n",code);//"12355"
free(code);
code=getcode("CODE=12345-MODE-12453-CODE1-12355", "CODE=*****");
printf("\"%s\"\n",code);//"12345"
free(code);
code=getcode(NULL, "CODE1-*****");
printf("\"%s\"\n",code);//"12355"
free(code);
return 0;
}

Resources