I am trying to parse a string from stdin such as this one { 7 , 3,5 ,11, 8, 16, 4, 9, 2
,8, 4, 2} ( there is a \n between 2 and 8 ).
I have made a function to extract the numbers and trim commas spaces and newlines (accepts char* as an input) but the problem is when I try to get input using scanf I can't get spaces so I used fgets instead but fgets will exit as soon as it sees \n.
Is there a way I can get a string from this ?
int nums[1000], count = 0;
char chr;
while(scanf("%c%d", &chr, &nums[count]) > 0) //there was at least one match
{
if(chr == '}')
break; //we have reached the end
if(chr != ',' && chr != '{')
continue; //skip spaces (} is an exception)
count++;
}
You can use fgets to read the full line and use strtok to read the numbers. The example below will also treat \n as a comma ,
char line[512];
char *buf = 0;
while(fgets(line, sizeof(line), stdin))
{
if(!strstr(line, "{") && !buf)
continue;
if(!buf)
{
buf = strdup(line);
}
else
{
buf = realloc(buf, strlen(buf) + strlen(line) + 1);
strcat(buf, line);
}
if(strstr(line, "}"))
{
char *token = strtok(buf, "{");
strtok(buf, ",}\n");
while(token)
{
int n = 0;
sscanf(token, "%d", &n);
printf("%d, ", n);
token = strtok(NULL, ",}\n");
}
free(buf);
break;
}
}
Related
I am trying to figure out how to read input multiple ways. Input can look like this.
N:{-4,2,1}
E:{1,1,9}
W:{-2,5,3}
S:{7,1}
or like this
E:{9,1,1}N:{1,2,-4}W:{3,5,-2}S:{7,1}
I added into my code checking for word END. Its supposed to be working on EOF but on my windows machine it doesnt work. So input END to signal END of input. So far I managed to get them both working separatly... But I need them to both at the same time.
char *skip_whitespace(char *str)
{
//skip any leading whitespace characters
while (*str == ' ' || *str == '\t')
str++;
return str;
}
void read_tokens(int *north, int *west, int *east, int *south, int *north_size, int *west_size, int *east_size, int *south_size)
{
//buffer for reading in input
char buffer[MAX_TOKENS];
//read in the input line by line
while (fgets(buffer, MAX_TOKENS, stdin) != NULL)
{
//remove the newline character from the end of the line
buffer[strcspn(buffer, "\n")] = 0;
//check for the "END" string to end the input
if (strcmp(buffer, "END") == 0)
break;
//split the line at the curly brace character
char *direction_token = strtok(buffer, "{");
char *tokens = strtok(NULL, "}");
//get the direction
char direction = direction_token[0];
//skip any leading or trailing whitespace characters
tokens = skip_whitespace(tokens);
//split the tokens at each comma
char *token = strtok(tokens, ",");
//determine the direction and store the tokens in the appropriate array
if (direction == 'N')
{
while (token != NULL)
{
//skip any leading or trailing whitespace characters
token = skip_whitespace(token);
//store the token in the array
north[*north_size] = atoi(token);
(*north_size)++;
//find the next token
token = strtok(NULL, ",");
}
}
else if (direction == 'W')
{
while (token != NULL)
{
//skip any leading or trailing whitespace characters
token = skip_whitespace(token);
//store the token in the array
west[*west_size] = atoi(token);
(*west_size)++;
//find the next token
token = strtok(NULL, ",");
}
}
else if (direction == 'E')
{
while (token != NULL)
{
//skip any leading or trailing whitespace characters
token = skip_whitespace(token);
//store the token in the array
east[*east_size] = atoi(token);
(*east_size)++;
//find the next token
token = strtok(NULL, ",");
}
}
else if (direction == 'S')
{
while (token != NULL)
{
//skip any leading or trailing whitespace characters
token = skip_whitespace(token);
//store the token in the array
south[*south_size] = atoi(token);
(*south_size)++;
//find the next token
token = strtok(NULL, ",");
}
}
else
{
//invalid direction = error
printf("Nespravny vstup.\n");
}
}
}
Here is the main function. For anyone intrested here is calling and printing.
int main(void)
{
//field for token values
int north[MAX_TOKENS], west[MAX_TOKENS], east[MAX_TOKENS], south[MAX_TOKENS];
//sizes of token value fields
int north_size = 0, west_size = 0, east_size = 0, south_size = 0;
printf("Input:\n");
//fetch token values from input
read_tokens(north, west, east, south, &north_size, &west_size, &east_size, &south_size);
printf("N: { ");
for (int i = 0; i < north_size; i++)
printf("%d, ", north[i]);
printf("}\n");
printf("W: { ");
for (int i = 0; i < west_size; i++)
printf("%d, ", west[i]);
printf("}\n");
printf("E: { ");
for (int i = 0; i < east_size; i++)
printf("%d, ", east[i]);
printf("}\n");
printf("S: { ");
for (int i = 0; i < south_size; i++)
printf("%d, ", south[i]);
printf("}\n");
}
You have an input format that permits optional whitespace, including newlines, between tokens. Your two examples differ only in that one makes use of that option and the other doesn't. In the most basic terms, then, the solution is to make your parser ignore (all) whitespace between tokens, too. Such a parser handles both forms of input presented, and other variants, too.
I do think, however, that fgets() is more a liability here than a help. Your input is not fundamentally line-oriented, and with fgets() you need to (but do not presently) watch out for and handle cases where a long line is split over two or more reads. I suggest tokenizing the input directly from the stream instead of first reading it into an intermediate buffer.
I know that scanf() gets a lot of hate, but it might serve your purpose pretty well here. It already knows how to skip whitespace, to recognize integers, and to match specific characters. Something along these lines, maybe:
while (1) {
char direction;
char delim;
int result;
// scan first part:
result = scanf(" %c : %c", &direction, &delim);
if (result == EOF) {
// end of file
} else if (result != 2 || !is_valid_direction(direction) || delim != '{') {
// invalid input ...
// ... or maybe (part of) an "END" keyword if you decide to
// go ahead with that
}
// ... handle direction code ...
do {
// scan number
int num;
result = scanf("%d %c", &num, &delim);
if (result != 2 || (delim != ',' && delim != '}')) {
// invalid input ...
// unless empty number lists are allowed: N:{}
}
// store number ...
} while (delim == ',');
}
If you want to avoid scanf(), then you can do basically the same thing by reading one character at a time via getchar() or fgetc(). Or, yes, with fgets() too, provided you exercise sufficient care.
That's schematic, of course, not a full implementation of the needed parser
#include <stdio.h>
#include <string.h>
int main() {
int counter1, counter2;
char line[200] = ""; //store all words that don't need to be deleted
char deleteWord[100]; //word that needs to be deleted
char space;
char word[100];
scanf("%s", deleteWord);
while (1) {
scanf("%s", word);
if (feof(stdin))
break;
// increment counter of total words
++counter1;
if (strcmp(word, deleteWord) == 0) {
// see if the word read in == delete word
// increment counter of deleted words
++counter2;
} else
if (strcmp(word, " ") == 0) { // space is an actual space
strcat(line, word);
strcat(line, " ");
} else
if (strcmp(word, "\n")) { // space a new line \n
strcat(line, word);
strcat(line, "\n");
}
}
printf("--NEW TEXT--\n%s", line);
return 0;
}
In summary, my code is supposed to remove a user input string (one or more words) from another user input string (containing or not containing the word(s)) and produce the output. The code removes the word but it adds a newline per word for each iteration. I believe it is doing this because the expression for the second else if is always true. However, when I properly add the strcmp function for the second else if statement, the code does not produce an output at all (no compiler errors - just missing input). Why is this happening and how do I do a strcmp function for a newline?
Your read the words with scanf("%s", word), which poses these problems:
all white space is ignored, so you cannot test for spaces nor newlines as you try to do in the loop, and you cannot keep track of line breaks.
you should tell scanf() the maximum number of bytes to store into the destination array word, otherwise any word longer than 99 characters will cause a buffer overflow and invoke undefined behavior.
you should test the return value of scanf() instead of callin feof() which might be true after the last word has been successfully read. You should simply write the loop as
while (scanf("%99s", word) == 1) {
// increment counter of total words
++counter1;
...
you do not test if the words fit in the line array either, causing a buffer overflow if the words kept amount to more than 199 characters including separators.
To delete a specific word from a stream, you could read one line at a time and delete the matching words from the line:
#include <ctype.h>
#include <stdio.h>
#include <string.h>
int main() {
char deleteWord[100]; //word that needs to be deleted
char line[1000]; //store all words that don't need to be deleted
printf("Enter the word to remove: ");
if (scanf("%99s", deleteWord) != 1)
return 1;
// read and discard the rest of the input line
int c;
while ((c = getchar()) != EOF && c != '\n')
continue;
size_t len = strlen(deleteWord);
printf("Enter the text: ");
while (fgets(line, sizeof line, stdin)) {
char *p = line;
char *q;
while ((p = strstr(p, deleteWord)) != NULL) {
if ((p == line || isspace((unsigned char)p[-1]))
&& (p[len] == '\0' || isspace((unsigned char)p[len]))) {
/* remove the word */
memmove(p, p + len, strlen(p + len) + 1);
} else {
p += len;
}
}
/* squeeze sequences of spaces as a single space */
for (p = q = line + 1; *p; p++) {
if (*p != ' ' || p[-1] != ' ')
*q++ = *p;
}
*q = '\0';
fputs(line, stdout);
}
return 0;
}
I am making a program to read a file and determine if a word is a palindrome. I am running into an issue where the last token has a trailing newline and won't register as a palindrome.
Here is the file input:
leVel CompUtER Science theORY radar
And this is my code:
#include<stdio.h>
#include<string.h>
void palindrome(char str[]){
int length = strlen(str);
int i = 0;
int j = length - 1;
for(i = 0; i < length; i++){
if(str[i] != str[j]){
printf("String %s is not a palindrome.\n", str);
return;
}
j--;
}
printf("String %s is a palindrome.\n", str);
return;
}
int main() {
char line1[100];
fgets(line1, 100, stdin);
printf("%s", line1);
char *token;
token = strtok(line1, " ");
while(token != NULL){
printf("%s\n", token);
palindrome(token);
token = strtok(NULL, " ");
}
Thanks for the help!
If you are using strtok, then you can use " \n" as the delimiter and the newline will be taken care of.
int main() {
char line1[100];
fgets(line1, 100, stdin);
printf("%s", line1);
const char *delim = " \n";
char *token;
token = strtok(line1, delim);
while(token != NULL){
printf("%s\n", token);
palindrome(token);
token = strtok(NULL, delim);
}
...
}
Another great method to remove the newline is to use strcspn like this:
char line[1024];
fgets(line, sizeof line, stdin);
line[strcspn(line, "\n")] = 0; // removing newline if one is found
Why not just use fgetc and stop at the newline? You could also even just find the newline character in the string and assign '\0' to it, and it will be gone.
I have this code:
int main(){
char buf[40];
char buff[40];
char bufff[40];
fgets(buf, 40, stdin);
fgets(buff, 40, stdin);
fgets(bufff, 40, stdin);
}
input:
Hello
from
Earth
I must have this output:
Hello
from
Earth
Hello from Earth
I send the code to a valutation platform, and it returned to me that with the following code I'll get wrong output:
buf[strlen(buf)-1] = "";
buff[strlen(buff)-1] = "";
bufff[strlen(bufff)-1] = "";
printf("%s\n%s\n%s\n", buf, buff, bufff);
printf("%s %s %s", buf, buff, bufff);
"" is a string literal, which is an array, and it will be converted to integer in implementation-defined manner. You should use '\0' as NUL character.
The last characters of lines need not be newline characters.
You will have to remove the spaces in the input to match the output.
Try this:
#include <stdio.h>
#include <string.h>
int main(void){
/* initialize to avoid undefined behavior when no data is read */
char buf[40] = "";
char buff[40] = "";
char bufff[40] = "";
char *lf;
/* read the input */
fgets(buf, 40, stdin);
fgets(buff, 40, stdin);
fgets(bufff, 40, stdin);
/* remove newline characters if they exists */
if ((lf = strchr(buf, '\n')) != NULL) *lf = '\0';
if ((lf = strchr(buff, '\n')) != NULL) *lf = '\0';
if ((lf = strchr(bufff, '\n')) != NULL) *lf = '\0';
/* remove space characters: implement here to match the actual specification */
if ((lf = strchr(buf, ' ')) != NULL) *lf = '\0';
if ((lf = strchr(buff, ' ')) != NULL) *lf = '\0';
if ((lf = strchr(bufff, ' ')) != NULL) *lf = '\0';
/* print */
printf("%s\n%s\n%s\n", buf, buff, bufff);
printf("%s %s %s", buf, buff, bufff);
return 0;
}
Omitted in this code, you should check if readings are successful.
I've created this simple function for removing right-trailing whitespace in general. Should also remove additional spaces. And you can even adapt it easily to filter out other symbols you want.
#define IS_WHITESPACE(Char) (Char == ' ' || Char == '\n' || Char == '\t')
void trim_right(char *string)
{
int i;
for (i = strlen(string) - 1; i >= 0; i++)
if (IS_WHITESPACE(string[i]))
string[i] = '\0';
else
break;
}
I have created the two following functions. The first, eatWrd, returns the first word in a string without any white spaces, and removes the first word from the input string:
MAX is a number representing the max length of a string
char* eatWrd(char * cmd)
{
int i = 0; //i will hold my place in cmd
int count = 0; //count will hold the position of the second word
int fw = 0; //fw will hold the position of the first word
char rest[MAX]; // rest will hold cmd without the first word
char word[MAX]; // word will hold the first word
// start by removing initial white spaces
while(cmd[i] == ' ' || cmd[i] == '\t'){
i++;
count++;
fw++;
}
// now start reading the first word until white spaces or terminating characters
while(cmd[i] != ' ' && cmd[i] != '\t' && cmd[i] != '\n' && cmd[i] != '\0'){
word[i-fw] = cmd[i];
i++;
count++;
}
word[i-fw] = '\0';
// now continue past white spaces after the first word
while(cmd[i] == ' ' || cmd[i] == '\t'){
i++;
count++;
}
// finally save the rest of cmd
while(cmd[i] != '\n' && cmd[i] != '\0'){
rest[i-count] = cmd[i];
i++;
}
rest[i-count] = '\0';
// reset cmd, and copy rest back into it
memset(cmd, 0, MAX);
strcpy(cmd, rest);
// return word as a char *
char *ret = word;
return ret;
}
The second, frstWrd, just returns the first word without modifying the input string:
// this function is very similar to the first without modifying cmd
char* frstWrd(char * cmd)
{
int i = 0;
int fw = 0;
char word[MAX];
while(cmd[i] == ' ' || cmd[i] == '\t'){
i++;
fw++;
}
while(cmd[i] != ' ' && cmd[i] != '\t' && cmd[i] != '\n' && cmd[i] != '\0'){
word[i-fw] = cmd[i];
i++;
}
word[i-fw] = '\0';
char *ret = word;
return ret;
}
To test the function, I used fgets to read a string from the User(me), and then I printed three strings (frstWrd(input), eatWrd(input), eatWrd(input)). I would have expected that given a string, "my name is tim" for example, the program would print "my my name", but instead it prints the third word three times over, "is is is":
// now simply test the functions
main()
{
char input[MAX];
fgets(input, MAX - 1, stdin);
printf("%s %s %s", frstWrd(input), eatWrd(input), eatWrd(input));
}
I have looked over my functions over and over and cannot see the mistake. I believe there is simply something I don't know about printf, or about using multiple string modification functions as arguments in another function. Any insight would be helpful thanks.
As I see rest and word are local variables in the function eatWrd. So it is bad practice to return pointer to such memory outside functions.
EDIT 1:
Also you should understand, that in line
printf("%s %s %s", frstWrd(input), eatWrd(input), eatWrd(input));
function eatWrd(input) could be called the first (before frstWrd(input)).
EDIT 2:
This can be usefull in finction eatWrd
//char rest[MAX]; // rest will hold cmd without the first word
char * rest = (char*) malloc(MAX);
And new main let be as:
int main()
{
char input[MAX];
fgets(input, MAX - 1, stdin);
printf("%s ", frstWrd(input));
printf("%s ", eatWrd(input));
printf("%s\n", eatWrd(input));
}
And in the end my solution for frstWrd (just to show how standard functions can be useful):
char* frstWrd(char * cmd)
{
char * word = (char *) malloc(MAX);
sscanf(cmd, "%s", word);
return word;
}