I am having issues with the get_string function - c

I just finally got the cs50 library working after a lot of trial on my Windows Vscode. Now, the problem is that the get_string function would not work as used below:
int main(void)
{
string s = get_string("Enter string: ");
// ensure string was read
if (s == NULL)
{
return 1;
}
string next = get_string("You just entered %s. Enter a new string: ", s);
if (next == NULL)
{
return 1;
}
printf("Your last string was %s\n", s);
}
When I write
string name = get_string("Give me a name:");
I get the error
In file included from hello.c:1:0:
cs50.c:78:8: note: expected ‘char **’ but argument is of type ‘char *’
string get_string(va_list *args, const string format, ...)
^~~~~~~~~~
hello.c:10:16: error: too few arguments to function ‘get_string’
string name = get_string("Give me a name:");
^~~~~~~~~~
In file included from hello.c:1:0:
cs50.c:78:8: note: declared here
string get_string(va_list *args, const string format, ...)
Here is my code. I am basically testing the get_string function not necessary needed in the function.
#include "cs50.c"
#include "cs50.h"
#include <stdio.h>
#include <stdlib.h>
int main()
{
char str[20] = "#";
string name = get_string("Give me a name:");
printf("What height of pyramid \n");
int user;
if(scanf("%d", &user))
{
for (int i =0; i< 8; i++)
{
if(user <= 0 || user > 8 )
{
printf("Height: %d \n", user);
printf("Provide value between 1 and 8 \n");
scanf("%d", &user);
}
}
printf("\n");
int i;
for (i = 1; i <= user; i++) {
for(int k = user; k > i; k--){
putchar(' ');
}
int j;
for (j = 0; j < i; j++) {
putchar('#');
}
putchar('\n');
}
return EXIT_FAILURE;
}
}
I expect to write
string s = get_string("Enter string: ");
and get a prompt in the terminal when running the code.

I was able to get through this by including cs50.h and cs50.c from libcs50-8.0.3 which is the v8.0.3 that conform with what I want.
Everything is fine now.

In fact by trial and error did not bring you quite to the correct solution.
The problem is rather simple. The get_string you're supposed to use is a macro from cs50.h. The cs50.c removes this macro definition and defines another function by name get_string (yes, it is awful). The end result is that you cannot #include <cs50.c> to make the code work even in a single-file application.
What you need to do is to only
#include <cs50.h>
and add cs50.c as another translation unit in your project, i.e. if your main program is prog.c you will add cs50.c as a file in the same folder.

If you added cs50 library and even you get this error, I can give an advice like this:
For linux terminal;
clang -o file_name file_name.c -lcs50
source: Harvard College CS50

Copy and paste this code in your source code file:
My Own CS50 Get_String Function (Shortened Implementation)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#define SIZE_MAX 50
char *get_string(const char *format, ...)
{
if (format != NULL)
{
va_list ap;
va_start(ap, format);
vprintf(format, ap);
va_end(ap);
}
char *buffer = NULL;
size_t capacity = 0;
size_t size = 0;
unsigned read = 0;
int c;
while ((c = fgetc(stdin)) != '\r' && c != '\n' && c != EOF)
{
read++;
if (read > capacity && read < SIZE_MAX )
capacity++;
else
{
free(buffer);
return NULL;
}
char *temp = realloc(buffer, capacity);
if (temp == NULL)
{
free(buffer);
return NULL;
}
buffer = temp;
buffer[size++] = c;
}
if (size == 0 || c == '\n')
return NULL;
if (size == SIZE_MAX)
{
free(buffer);
return NULL;
}
char *s = realloc(buffer, size + sizeof(char));
if (s == NULL)
{
free(buffer);
return NULL;
}
s[size] = '\0';
return s;
}

This works for me:
#include <cs50.c>
#include <stdio.h>
int main(void)
{
char c1 = ' ', *c2, **c3;
c2 = &c1;
c3 = &c2;
string s = get_string(c3, "Some text: ");
printf("%s\n", s);
}

I got a workaround to this problem! Calling the string 's' variable within the 'get_string' function fixes this problem.
#include <cs50.c>
#include <cs50.h>
#include <stdio.h>
int main(void)
{
printf("Enter a string: ");
string s = get_string(" ", s);
printf("%s", s);
Note:
Am not aware if this approach results into bugs down the line. It works fine for me.

Related

How to make a function to input a string with unknown length in c with type void

I am trying to make a function that input an unknown length of a string , but I don't want it to return anything I want it to make changes by using the pointer that I pass.
this was my attempt .
#include <stdlib.h>
#include <stdio.h>
void get(char * string){
int size=1;
string = malloc(size);
char c;
int i=0;
while(1){
c = getchar();
if(c=='\n'){break;}
string[i] = c;
i++;
string = realloc(string , ++size);
}
string[i] = '\0';
}
int main(){
char *buff;
printf("String :");
get(buff);
printf("%s" , buff);
return 0;
}
the output on my gcc windows os :
PE
1- what is the PE
2- what is wrong here
3- is the c=='\n' line good for the test if the user pressed an enter or should i use EOF or something else
I made a few changes to your code so that it would use the pointer passed to it and handle a possible EOF in the input:
#include <stdlib.h>
#include <stdio.h>
void get(char **string){
char *input = NULL;
int result, index = 0;
while (1) {
result = getchar();
if (EOF == result) { printf("\n"); break; }
if ('\n' == result) break;
char *temp = realloc(input , index + 2);
if (NULL == temp) {
perror("Could not increase memory for string");
if (input) free(input);
exit(1);
}
input = temp;
input[index++] = (char) result;
}
if (input) {
input[index] = '\0';
*string = input;
}
}
int main(void) {
char *buff = NULL;
printf("String : ");
get(&buff);
if (buff) {
printf("%s\n" , buff);
free(buff);
}
return 0;
}
Output
$ ./main
String : a short string
a short string
$ ./main
String : input with EOF
input with EOF
Note
I do my best to handle error conditions, but I am not certain I caught everything.

Why doesn't the code run with a test.in file created by Sublime TextEditor

This was a piece of code I have written for my assignment, some of the weird code design are not controllable by me. I am currently writing these on MacOS.
file1
#include <stdio.h>
extern int read_palindrome();
int main()
{
if (read_palindrome()) printf("input is a palindrome");
else printf("input is not a palindrome");
return 0;
}
file2
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int check_palindrome2(char *, int);
// malloc() will be used as usual to set aside an initial memory
// The entire input will be read gradually by characters using getchar()
// In the event we require more memory than what we have previously,
// use realloc() to increase memory size dynamically
int read_palindrome() {
unsigned int len_max = 128;
unsigned int current_size = 0;
char *pStr = malloc(len_max);
current_size = len_max;
int i = 0;
int c = EOF;
if (pStr == NULL) {
return -1;
}
while (( c = getchar() ) != '\n') {
pStr[i] = (char)c;
i++;
if(i == current_size) {
current_size += len_max;
char *tmp = realloc(pStr, current_size);
if (tmp == NULL) {
free(pStr);
return -1;
}
pStr = tmp;
}
}
int retval = check_palindrome2(pStr,i);
free(pStr);
return retval;
}
int check_palindrome2(char *s, int length) {
for (int i = 0; i < length / 2; i++) {
if (s[i] != s[length-i-1])
return 0;
}
return 1;
}
I would think this code works except for empty files, which will cause my program to continuously expect input and not terminate. However, I realised when using Sublime Text, creating a test.in file without pressing "Enter" somehow displays the "non-terminating" behaviour as well, while typing something in vim without pressing "Enter" for a newline still allows the code to work. Does anyone know the reason behind this phenomenon?

strstr == NULL doesn't work,

#include <stdio.h>
#include <string.h>
#define N 5
char username[N+3][20]={"ana","sofia","maria","isabel","joao","hugo","francisco","pedro"};
char str[20];
read_username()
{
printf("Insert your username: ");
gets(str);
}
void searchusername(int n)
{
int i;
for(i=0;i<=n;i++)
{
if(strstr(username[i], str) != NULL)
printf("username exists")
}
}
int main()
{
read_username();
searchusername(8);
}
I have the code to check if username exists, but i can´t seem to turn it around so i only get the printf when username doesn't exist, any other way without using NULL is also okay, ty.
One issue is that you are not avoiding a buffer overflow with gets(). I have to assume for this example that you are typing in short usernames that do not exceed 19 characters. Anything longer is going to cause problems if you don't account for it.
More importantly, you are not comparing the usernames correctly. You should not be using strstr() for that purpose. It searches for a substring inside of another string, it does not compare strings. For example, if you typed in ia, strstr() would match with both sofia and maria, both of which are wrong results for a username lookup. Use strcmp() for comparisons.
Try something more like this instead:
#include <stdio.h>
#include <string.h>
#define N 8
char* username[N] = {"ana", "sofia", "maria", "isabel", "joao", "hugo", "francisco", "pedro"};
char str[20] = {0};
void read_username()
{
printf("Insert your username: ");
if (fgets(str, 20, stdin))
{
int len = strlen(str);
if ((len > 0) && (str[len-1] == '\n'))
str[len-1] = '\0';
}
}
void searchusername()
{
for(int i = 0; i < N ; i++)
{
if (strcmp(username[i], str) == 0)
{
printf("username exists");
return;
}
}
printf("username does not exist");
}
int main()
{
read_username();
searchusername();
}
Your compare failed because of the '\n' character.
When using gets and fgets you should trim the input.
You can use function like this:
#include <ctype.h> //for isspace
char* trim(char *input_string)
{
int i=0;
char *retVal = input_string;
i = strlen(input_string)-1;
while( i>=0 && isspace(input_string[i]) ){
input_string[i] = 0;
i--;
}
i=0;
while(*retVal && isspace(retVal[0]) ){
retVal ++;
}
return retVal;
}

Erase last members of line from text file

I have a text file as data.txt and I want to delete the last members of each line:
Here's the text file:
2031,2,0,0,0,0,0,0,54,0,
2027,2,0,0,0,0,0,0,209,0,
2029,2,0,0,0,0,0,0,65,0,
2036,2,0,0,0,0,0,0,165,0,
I would like to delete so it becomes:
2031,2,0,0,0,0,0,0,
2027,2,0,0,0,0,0,0,
2029,2,0,0,0,0,0,0,
2036,2,0,0,0,0,0,0,
I'm working in C but as the numbers can have two or three digits, I'm not sure how to do this.
A couple of uses of strrchr() can do the job:
#include <string.h>
void zap_last_field(char *line)
{
char *last_comma = strrchr(line, ',');
if (last_comma != 0)
{
*last_comma = '\0';
last_comma = strrchr(line, ',');
if (last_comma != 0)
*(last_comma + 1) = '\0';
}
}
Compiled code that seems to work. Note that given a string containing a single comma, it will zap that comma. If you don't want that to happen, then you have to work a little harder.
Test code for zap_last_field()
#include <string.h>
extern void zap_last_field(char *line);
void zap_last_field(char *line)
{
char *last_comma = strrchr(line, ',');
if (last_comma != 0)
{
*last_comma = '\0';
last_comma = strrchr(line, ',');
if (last_comma != 0)
*(last_comma + 1) = '\0';
}
}
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *line = malloc(4096);
if (line != 0)
{
while (fgets(line, 4096, stdin) != 0)
{
printf("Line: %s", line);
zap_last_field(line);
printf("Zap1: %s\n", line);
}
free(line);
}
return(0);
}
This has been vetted with valgrind and is OK on both the original data file and the mangled data file listed below. The dynamic memory allocation is there to give valgrind the maximum chance of spotting any problems.
I strongly suspect that the core dump reported in a comment happens because the alternative test code tried to pass a literal string to the function, which won't work because literal strings are not generally modifiable and this code modifies the string in situ.
Test code for zap_last_n_fields()
If you want to zap the last couple of fields (a controlled number of fields), then you'll probably want to pass in a count of the number of fields to be zapped and add a loop. Note that this code uses a VLA so it requires a C99 compiler.
#include <string.h>
extern void zap_last_n_fields(char *line, size_t nfields);
void zap_last_n_fields(char *line, size_t nfields)
{
char *zapped[nfields+1];
for (size_t i = 0; i <= nfields; i++)
{
char *last_comma = strrchr(line, ',');
if (last_comma != 0)
{
zapped[i] = last_comma;
*last_comma = '\0';
}
else
{
/* Undo the damage wrought above */
for (size_t j = 0; j < i; j++)
*zapped[j] = ',';
return;
}
}
zapped[nfields][0] = ',';
zapped[nfields][1] = '\0';
}
#include <stdio.h>
int main(void)
{
char line1[4096];
while (fgets(line1, sizeof(line1), stdin) != 0)
{
printf("Line: %s", line1);
char line2[4096];
for (size_t i = 1; i <= 3; i++)
{
strcpy(line2, line1);
zap_last_n_fields(line2, i);
printf("Zap%zd: %s\n", i, line2);
}
}
return(0);
}
Example run — using your data.txt as input:
Line: 2031,2,0,0,0,0,0,0,54,0,
Zap1: 2031,2,0,0,0,0,0,0,54,
Zap2: 2031,2,0,0,0,0,0,0,
Zap3: 2031,2,0,0,0,0,0,
Line: 2027,2,0,0,0,0,0,0,209,0,
Zap1: 2027,2,0,0,0,0,0,0,209,
Zap2: 2027,2,0,0,0,0,0,0,
Zap3: 2027,2,0,0,0,0,0,
Line: 2029,2,0,0,0,0,0,0,65,0,
Zap1: 2029,2,0,0,0,0,0,0,65,
Zap2: 2029,2,0,0,0,0,0,0,
Zap3: 2029,2,0,0,0,0,0,
Line: 2036,2,0,0,0,0,0,0,165,0,
Zap1: 2036,2,0,0,0,0,0,0,165,
Zap2: 2036,2,0,0,0,0,0,0,
Zap3: 2036,2,0,0,0,0,0,
It also correctly handles a file such as:
2031,0,0,
2031,0,
2031,
2031
,

C parser for data separated with comma

What is the most effective way to write a parser in C for data with the following format:
atr#1,atr#2,...,atr#n
btr#1,btr#2,...,btr#n
...
Each record is in new line and attributes are separated with comma.
What function should be used? Do you have any examples?
Here's some example code that will read the file sparated by newlines line by line, then split the arguments and print them out (you can easily adapt it to, for example, parse it to an array of array of char *s):
#include <stdio.h>
#include <string.h>
int main()
{
FILE *f = fopen("file.txt", "r");
char ptr[1024];
char *token;
while (fgets(ptr, 1024, f) != NULL)
{
token = strtok(ptr, ",");
while(token)
{
printf("Token: %s\n", token);
token = strtok(NULL, ",");
}
}
fclose(f);
return 0;
}
This will work:
/* You need the following includes and defines */
#include <stdio.h>
#include <iostream.h>
#include <string.h>
#define NULL_CHAR 0x0
int parse(char* data) {
const int LINE_SIZE=255; /* Should be long enough for your unparsed data */
const int MAX_FIELDS=99; /* Maximum number of fields */
char output[MAX_FIELDS][LINE_SIZE];
int i;
int output_field_count;
int output_char_idx;
for (i = 0; i < MAX_FIELDS; i++) {
strcpy(output[i], "");
}
output_field_count = 0;
output_char_idx = 0;
for (i = 0; i < LINE_SIZE; i++) {
if ((data[i] != ',') &&
(output_field_count < MAX_FIELDS) &&
((output_char_idx+1) < LINE_SIZE)) {
output[output_field_count][output_char_idx] = data[i];
output[output_field_count][output_char_idx+1] = NULL_CHAR;
output_char_idx++;
}
else if (data[i] == ',') {
output_field_count++;
output_char_idx = 0;
}
}
output_field_count++;
output_char_idx = 0;
printf("OUTPUT FIELD COUNT IS: %d\n", output_field_count);
for (i = 0; i < output_field_count; i++) {
printf("FIELD %i IS: %s\n", i, output[i]);
}
return 0;
}
This can be called as follows:
char data[500]; /* Should be long enough for your unparsed data */
strcpy(data, "atr#1,atr#2,...,atr#n");
parse(data);
strcpy(data, "btr#1,btr#2,...,btr#n");
parse(data);
Pick the right tool for the job. It's about one line in Perl, Python, or best yet awk. If you have a compelling reason to use C, please explain in your post - otherwise I think the most judicious answer anyone can give you is to advise you to pick the right tool for the job instead of asking how to do something onerous in a language that's bad at it.
From the command line:
tr ',' '\n' < file.txt
Will turn the commas into new lines.

Resources