Splitting a string from a input without using strtok() - c

I have been working on a program to read zip codes and populations from a .csv file and find the zip code with the largest and smallest population. I'm tasked to do this without using scanf(all variants) and strtok(). Currently, it outputs the entire string and I do not know how to split the string into just zip code and population. Any help is appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
int lines = 1024; //Maximum lines, specified
char str[256]; //Maximum characters, specified
char* pEnd = str;
//Variables for parsing data
char zipCode[256];
char population[256];
long bufPopulation = 0; //Variable to convert from string to long
//Variables to track the number of zipcodes and total population
static int NumOfZipCodes = 0;
static long totalPopulation = 0;
for (int i = 0; i < lines && fgets(str, sizeof(str), stdin); i++){
//parses the data being read into variable 'str'
strcpy(zipCode, str);
strcpy(population, str);
printf("String %s", str); //outputs str
printf("Zip codes %s", zipCode); //outputs zipCode
printf("Population %s", population); //outputs population
if (i > 0){
bufPopulation = strtol(population, &pEnd, 10);
printf("Buffer population %.0ld", bufPopulation); //outputs the long value of population
totalPopulation += bufPopulation;
NumOfZipCodes++;
}
}
int size = sizeof population/sizeof population[0];
long maxPop, minPop = 0;
char maxZip, minZip = zipCode[0];
for (int i = 1; i < size; i++){
bufPopulation = strtol(population, &pEnd, 10);
if (minPop > bufPopulation)
{
minPop = bufPopulation;
minZip = zipCode[i];
}
if (maxPop < bufPopulation)
{
maxPop = bufPopulation;
maxZip = zipCode[i];
}
}
//Displays the stored values
printf("Total Population across %d zipcodes is ", NumOfZipCodes);
printf("%.0ld\n", totalPopulation);
printf("The fewest people live in %.0d, population ", minZip);
printf("%.0ld\n", minPop);
printf("The most people live in %.0d, population ", maxZip);
printf("%.0ld\n", maxPop);
return 0;
}

The strtol() function assigns endptr, which is pointed by the second
argument, to the address of the remaining string after converting the valid
substring into the long integer. The character pointed by the address will
be normally the delimiter or the newline character at the end of the string.
Then you can repeat calling strtod() and skipping the delimiter to parse
the columns of the csv line. Would you please try the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXCHARS 256 // max characters in a line
#define DELIM ' ' // column delimiter
int main() {
char str[MAXCHARS]; // line buffer
char *eptr; // pointer within str
int zip, minZip, maxZip;
long pop, minPop, maxPop;
int numOfZipCodes = 0;
long totalPopulation = 0;
fgets(str, MAXCHARS, stdin); // read the header and discard
while (fgets(str, MAXCHARS, stdin)) { // read each line
zip = (int)strtol(str, &eptr, 10); // parse the zip code
if (*eptr != DELIM) { // check the next delimiter
fprintf(stderr, "illegal input string: %s", str);
exit(1);
}
while (*eptr == DELIM) eptr++; // skip the delimiter
pop = strtol(eptr, &eptr, 10); // parse the population
if (*eptr != DELIM) { // check the next delimiter
fprintf(stderr, "illegal input string: %s", str);
exit(1);
}
printf("Zip code %d\n", zip); // output zip code
printf("Population %ld\n", pop); // output population
if (numOfZipCodes == 0) { // initialize values on the 1st line
minPop = maxPop = pop;
minZip = maxZip = zip;
} else {
if (minPop > pop) {
minPop = pop;
minZip = zip;
}
if (maxPop < pop) {
maxPop = pop;
maxZip = zip;
}
}
totalPopulation += pop;
numOfZipCodes++;
}
// display the results
printf("Total Population across %d zipcodes is %ld\n", numOfZipCodes, totalPopulation);
printf("The fewest people live in %d, population %ld\n", minZip, minPop);
printf("The most people live in %d, population %ld\n", maxZip, maxPop);
return 0;
}
It assumes the delimiter is a whitespace as shown in your comment. If the
deimiter is a comma or other character, modify the #define DELIM ' ' line
accordingly.

Related

String Tokenization problem occurs when deleting duplicate words from a string

What I'm trying to do in the following code is to tokenize a string and store every token in a dynamic allocated structure but exclude any duplicates.
This code kind of works, until I enter a string that contains two equal words. For example, the string "this this", will also store the second word even though it's the same. But if I enter "this this is" instead, it removes the second "this", and completely ignores the last word of the string, so that it doesn't get deleted if there's a duplicate in the string.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define dim 70
typedef struct string {
char* token[25];
} string;
int main() {
string* New = malloc(dim*sizeof(string));
char* s;
char* buffer = NULL;
int i = 0, r = 0;
s = malloc(dim * sizeof(char));
fgets(s, dim, stdin);
printf("The string is: %s\n", s);
New->token[i] = malloc(dim*sizeof(char));
New->token[i] = strtok(s, " ");
++i;
while((buffer = strtok(NULL, " ")) && buffer != NULL){
printf("\nbuffer is: %s", buffer);
for(r = 0; r < i; ++r) {
if(strcmp(New->token[r], buffer) != 0 && r == i-1) {
New->token[i] = malloc(strlen(buffer)*sizeof(char)+1);
New->token[i] = buffer;
++i;
}
else if(New->token[r] == buffer) {
break;
}
}
}
printf("\n New string: ");
for(i = 0; New->token[i] != NULL; ++i) {
printf(" %s", New->token[i]);
}
return 0;
}
In my mind this should work fine but I'm really having a hard time finding what I did wrong here. If you need additional info just ask me please, I apologise for any eventual lack of clarity (and for my english).
Complete re-write of this answer to address some fundamentally wrong things I did not see the first time through. See in-line comments in the code at bottom to explain some of the construct changes:
I ran your code exactly as is and saw what you are describing, and other than the note about using strcmp in the other answer, found several lines of code that can be adjusted, or removed to make it do what you described it should:
First, the struct definition creates a pointer to an array of char. Based on what you are doing later in the code, what you need is a simple array of char
typedef struct string {
//char* token[25]; //this create a pointer to array of 25 char
char token[25]; //this is all you need
} string;
As you will see later, this will greatly simplify memory allocation.
some basic problems:
Include the \n newline character in your parsing delimiter. When <enter> is hit as the end of entering the string, a newline is appended, causing the first instance of this and the second instance of this\n to be unequal.
while((buffer = strtok(NULL, " \n")) && buffer != NULL){
^^
This line is creating uninitialized memory.
string* New = malloc(dim*sizeof(string));
A note about using malloc() vs. calloc(): malloc() leaves the memory it creates uninitialized, while calloc() creates a block of memory all initialized to 0.
Memory created using malloc()
Memory created using calloc():
This becomes important in several places in your code, but in particular I see a problem in the last section:
for(i = 0; New->token[i] != NULL; ++i) {
printf(" %s", New->token[i]);
}
If the memory created for New is not initialized, you can get a run-time error when the index i is incremented beyond the area in memory that you have explicitly written to, and loop attempts to test New->token[i]. If New->token[i] contains anything but 0, it will attempt to print that area of memory.
You should also free each instance of memory created in your code with a corresponding call to free().
All of this, and more is addressed in the following re-write of your code:
(tested against this is a string a string.)
typedef struct string {
//char* token[25]; //this create a pointer to array of 25 char
char token[25]; //this is all you need
} string;
int main() {
char* s;
char* buffer = NULL;
int i = 0, r = 0;
string* New = calloc(dim, sizeof(string));//Note: This creates an array of New.
//Example: New[i]
//Not: New->token[i]
s = calloc(dim , sizeof(char));
fgets(s, dim, stdin);
printf("The string is: %s\n", s);
buffer = strtok(s, " \n");
strcpy(New[i].token, buffer); //use strcpy instead of = for strings
//restuctured the parsing loop to a more conventional construct
// when using strtok:
if(buffer)
{
++i;
while(buffer){
printf("\nbuffer is: %s", buffer);
for(r = 0; r < i; ++r) {
if(strcmp(New[r].token, buffer) != 0 && r == i-1) {
strcpy(New[i].token, buffer);
++i;
}
else if(strcmp(New[r].token, buffer)==0) {
break;
}
}
buffer = strtok(NULL, " \n");
}
}
printf("\n New string: ");
for(i = 0; i<dim; i++) {
if(New[i].token) printf(" %s", New[i].token);
}
free(New);
free(s);
return 0;
}
You comparing pointers instead of comparing strings. Replace
}
else if(New->token[r] == buffer) {
break;
With
}
else if(strcmp(New->token[r], buffer) == 0) {
break;
You also need to copy the buffer:
memcpy(New->token[i],buffer,strlen(buffer)+1);
instead of
New->token[i] = buffer;
or replace both lines (along with malloc) with
New->token[i] = strdup(buffer);
And it's better to replace strtok with strtok_r (strtok is not re-entrant).
The structure seems unnecessary.
This uses an array of pointers to store the tokens.
The input can be parsed with strspn and strcspn.
Unique tokens are added to the array of pointers.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DIM 70
int main() {
char* token[DIM] = { NULL};
char s[DIM];
char* buffer = s;
int unique = 0, check = 0;
int match = 0;
int loop = 0;
size_t space = 0;
size_t span = 0;
fgets(s, DIM, stdin);
printf("The string is: %s\n", s);
while ( unique < DIM && *buffer){//*buffer not pointing to zero terminator
space = strspn ( buffer, " \n\t");//leading whitespace
buffer += space;//advance past whitespace
span = strcspn ( buffer, " \n\t");//not whitespace
if ( span) {
printf("\ntoken is: %.*s", (int)span, buffer );//prints span number of characters
}
match = 0;
for ( check = 0; check < unique; ++check) {
if ( 0 == strncmp ( token[check], buffer, span)) {
match = 1;//found match
break;
}
}
if ( ! match) {//no match
token[unique] = malloc ( span + 1);//allocate for token
strncpy ( token[unique], buffer, span);//copy span number of characters
token[unique][span] = 0;//zero terminate
++unique;//add a unique token
}
buffer += span;//advance past non whitespace for next token
}
printf("\n New string: ");
for( loop = 0; loop < unique; ++loop) {
printf(" %s", token[loop]);//print the unique tokens
}
printf("\n");
for( loop = 0; loop < unique; ++loop) {
free ( token[loop]);//free memory
}
return 0;
}

Operations with text and delimiters

The task is: to read the text from file and to read an array of delimiters from keyboard. Than the program should search the sequence of delimiters in the text and, if it would be found 3 times or more, swap all the odd strings in a circle. Also it should detete all the words, which exceed the length limit, entered by user, but only in odd strings.
This is what i've got now:
#include "stdafx.h"
#include <stdio.h>
#include <conio.h>
int main(void)
{
setlocale(LC_ALL, "Russian"); //entering the text
const int numberOfCharactersToRead = 128;
char* inputText = (char*)(malloc(sizeof(char) * numberOfCharactersToRead));
FILE *fp;
fopen_s(&fp, "D:\texxxt.txt", "r");
if (fp == NULL)
{
printf("nFile not foundn");
system("pause");
return 0;
}
fgets(inputText, numberOfCharactersToRead, fp);
printf("Enter the sequence of delimiters: "); //entering delimiters
const int numberOfDelimitersToRead = 6;
char* delimiters = (char*)(malloc(sizeof(char) * numberOfDelimitersToRead));
int indexer = 0;
for (indexer = 0; indexer < numberOfDelimitersToRead; indexer++)
{
delimiters[indexer] = getchar();
}
//Trying to use strtok in order to devide text into rows (unsuccesful)
char delims[] = "/n";
char *pch = strtok_s(NULL, inputText, &delims);
printf("nLexems:");
while (pch != NULL)
{
printf("n%s", pch);
pch = strtok_s(NULL, inputText, &delims);
}
return 0;
}
int symcount(void)
{ //function searching the quantity of delimiters
char str[20], ch;
int count = 0, i;
printf("nEnter a string : ");
scanf_s("%s", &str);
printf("nEnter the character to be searched : ");
scanf_s("%c", &ch);
for (i = 0; str[i] != ''; i++)
{
if (str[i] == ch)
count++;
}
if (count == 0)
printf("nCharacter '%c'is not present", ch);
else
printf("nOccurence of character '%c' : %d", ch, count);
return (0);
}
I dont really know how to devide the text into rows and how to make my program differentiate even and odd strings. I'm really confused
The definition of strtok_s is the following:
char *strtok_s(char *strToken, const char *strDelimit, char **context);
You are mixing up the parameters. The 1st parameter should be a pointer to the input string and the 2nd parameter should be the delimiter string. Finally after the function is executed the 3rd parameter will be passed a pointer to the string after the position where the delimiter was found, or NULL if no delimiter was found. This pointer can then be passed onto the next strtok_s call to continue the search.
char *pchNext;
char *pch = strtok_s(inputText, delimiters, &pchNext);
while (pch != NULL)
{
printf("\n%s", pch);
pch = strtok_s(NULL, delimiters, &pchNext); // The first parameter can be NULL here
}
Also, the textual representation of the newline character is \n, not /n or n.

How to scan a string for specific terms

I'm trying to scan user input text for specific words and then, when those words occur, print them to the console.
#import <Foundation/Foundation.h>
#include <stdio.h>
int main(){
char cArray[] = "example";
char cInput[] = "";
char cOutput[] = "";
printf("\nType your message:\n");
for (int y=0; y<1; y++){
fgets(cInput, 120, stdin);
}
printf("\nInitialised character array:\n");
for (int x=0; x<1; x++){
if(strncmp(&cInput[x], &cArray[x], 120) == 0){
strncpy(cOutput, cArray, strnlen(cInput, +1));
printf("%s\n", cOutput);
break;
}
}
}
Output:
Type your message:
example
Initialised character array:
Program ended with exit code: 120
Appreciate any feedback as I'm still learning :)
Thanks.
The edited code:
#include <stdio.h>
#include <string.h>
#define MAX_STR_LEN 120
int main(){
char *cArray[MAX_STR_LEN] = {"example", "this"};
char cInput[MAX_STR_LEN] = "";
char cOutput[MAX_STR_LEN] = "";
printf("Type your message:\n");
for (int y=0; y<1; y++){
fgets(cInput, MAX_STR_LEN, stdin);
char * ptr = cInput;
while((ptr=strstr(ptr, *cArray)) != NULL){
strncpy(cOutput, ptr, strlen(*cArray));
printf("Initialised string array:\n%s\n", cOutput);
ptr++;
}
}
}
Works although I'm encountering a different problem now. The output only seems to register one word before it completes, thus only "example" is printed.
Output:
Type your message:
this is an example
Initialised string array:
example
Program ended with exit code: 0
char cInput[] = "";
The sizeof this array is 1.
fgets(cInput, 120, stdin);
This is array out of bound write which will lead to undefined behavior.
Have
char cInput[120] = "";
You need to take care of
char cOutput[120] = "";
also. Since you are trying to write to this array after comparing.
You need strstr function from string.h
const char * strstr ( const char * str1, const char * str2 );
the following gives you an example of usage:
#include <stdio.h>
#include <string.h>
#define MAX_STR_LEN 120
int main(){
char cArray[MAX_STR_LEN] = "example"; // string to be searched in the input string
char cInput[MAX_STR_LEN] = ""; // input string
char cOutput[MAX_STR_LEN] = ""; // buffer for found string
printf("\nType your message:\n");
for (int y=0; y<1; y++){ // this loop from original example looks strange, but it works
fgets(cInput, MAX_STR_LEN, stdin);
}
// search in the input string
char * ptr;
if( ( ptr=strstr(cInput, cArray) ) != NULL)
{
//copy the string to cOutput
strncpy(cOutput, ptr, strlen(cArray));
// output the found string
printf("String that was found: \n%s\n", cOutput);
}
else
{
printf("String was not found in the input!\n");
}
}
EDIT:
If you want to all the strings, use the following loop instead of if-else:
// search in the input string
char * ptr = cInput;
while( ( ptr=strstr(ptr, cArray) ) != NULL)
{
//copy the string to cOutput
strncpy(cOutput, ptr, strlen(cArray));
// output the found string
printf("String \"%s\" was found at position %d\n", cOutput, (int)(ptr - cInput + 1));
// find next string
ptr++;
}

read 1/2/5 string inputs in c

i need to read input from the standart input line by line
but each line will contain 1 or 2 or 5 strings like :
bofob fbo
blabla bibi bobo fbo fbooo
bobobo bobo
bobof
how can i do this?
my idea is really not looking profassional and not working
char a[50],b[50],c[50],d[50],f[50];
int numOfStrings=0;
scanf(" %s",a); char a[50],b[50],c[50],d[50],f[50];
int numOfStrings=0;
scanf(" %s",a);
if (scanf (" %s",b)){
numOfStrings=2;
if (scanf (" %s %d %d",c,d,f)
numOfStrings=5;
}
if (scanf (" %s",b)){
numOfStrings=2;
if (scanf (" %s %d %d",c,d,f)
numOfStrings=5;
}
but its not working because it goes and read inputs from the next line
is there a way to read a whole line (i know its max 250 chars) and then know how many words are in there?
edit:
i will add a count words function
but what is the nicest wat ro read a line untilll the end line or eof??
int words(const char *sentence)
{
int count,i,len;
char lastC;
len=strlen(sentence);
if(len > 0)
{
lastC = sentence[0];
}
for(i=0; i<=len; i++)
{
if(sentence[i]==' ' && lastC != ' ')
{
count++;
}
lastC = int words(const char *sentence)
}
return count;
}
You need to use fgets() to take the input line-by-line. check the man page here. It will also liberate you from handling the limitation of [1/2/5/.....] numbers of space-seperated strings. Provided sufficient storage, you can read 1 to any number of "string"s.
Note: You might need to take care of the trailing newline \n [caused by ENTER] yourself. Causes trouble most of the times.
You could scan one line until the '\n' with %[^\n], then split the line into words with strtok():
#include <string.h>
#include <stdio.h>
const char s[2] = " ";
const int MAX_LINE_SIZE = 128;
FILE *fp;
char *word, *str;
int word_counter;
/* Open the file here */
while (fgets(str, MAX_LINE_SIZE, fp) != NULL)
{
word_counter = 0
/* get the first word */
word = strtok(str, s);
/* walk through other words */
while (word != NULL)
{
printf(" %s\n", word);
word_counter++;
word = strtok(NULL, s);
}
printf("This string contains %d words\n",word_counter);
}
/* END of FILE */
You can use fgets to read a file and strchr to count the number of spaces:
#include <stdio.h>
#include <string.h>
int main(void)
{
char s[250];
char *p;
FILE *f;
int i;
f = fopen("demo.txt", "r");
while ((p = fgets(s, sizeof s, f))) {
i = 0;
while ((p = strchr(p, ' '))) {
p++;
i++;
}
printf("%d spaces\n", i);
}
fclose(f);
return 0;
}

arrays from txt in c (long numbers)

400000000000;499999999999;VISA;
50000000;59999999;MASTERCARD;
67000000;67999999;MAESTRO;
fields: 1. Range Start 2. Range End, 3 Name.
[Start Range] and [End Range] fields can be from 1 to 16 characters (digits) in length.
The program's objective is as follows:
First Request to enter 16-digit card number.
Card number input, verification and processing use char [n] type (Simbol array)
Second:Check for an entry corresponding to the entered card number can be found in a text file if I enter 45000000000 it's between 400000000000 and 499999999999 so i need to put a text in a autput name VISA.
And i can't use long long types... as i undrstand i need to use arrays...
Third Request to enter the amount in the format "nnnn.mm", where nnnn-1 to 4 digits long amount of lats, but mm - 2-digit amount santims.
char input[32]; // = "100;200;first";
char name[10];
int min, max, c, i, k;
FILE *file;
file = fopen("gg.txt","r");
i=0;
while ((c=getc(file))!= EOF)
{
k=(int)c;
input[i]=k;
i++;
}
char* result = NULL;
char delims[] = ";";
result = strtok(input, delims);
// atoi() converts ascii to integer.
min = atoi(result);
result = strtok(NULL, delims);
max = atoi(result);
result = strtok(NULL, delims);
strcpy(name, result);
printf("Min=%d, Max=%d, Name=%s\n", min, max, name);
printf("input=%s\n",input);
printf("%d\n",i);
getch();
return 0;
this code given me by varunl works vith smal numbers ( the containing of gg.txt file is: 100;200;first), but a need smt else, enybody, can help me?
The trick is in padding the numbers to 16 digits, so you can compare them as strings. So if you read this:
67000000;67999999;MAESTRO;
in reality you have to consider it like this:
0000000067000000;0000000067999999;MAESTRO;
The same for the user input.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void FindCCType(const char *pnumber);
int main(int argc, char *argv[])
{
FindCCType("67000001");
return 0;
}
#define MAXLENGTH 16
char* PadLeft(const char *pnumber)
{
char *pnumber2 = (char*)malloc(MAXLENGTH + 1);
size_t len = strlen(pnumber);
if (len > MAXLENGTH)
{
/* Too many digits in credit card number */
exit(1);
}
strcpy(pnumber2 + MAXLENGTH - len, pnumber);
char *pbegin = pnumber2, *plast = pnumber2 + MAXLENGTH - len;
while (pbegin < plast)
{
*pbegin = '0';
pbegin++;
}
return pnumber2;
}
void FindCCType(const char *pnumber)
{
printf("Input: %s\n", pnumber);
char *pnumber2 = PadLeft(pnumber);
FILE *file = fopen("gg.txt","r");
char pline[1000];
while (fgets(pline, sizeof(pline), file) != NULL)
{
if (strlen(pline) + 1 == sizeof(pline))
{
/* Line too much long */
exit(2);
}
char *pmin = strtok(pline, ";");
char *pmax = strtok(NULL, ";");
char *pname = strtok(NULL, ";");
printf("Min: %s, Max: %s, Name: %s", pmin, pmax, pname);
char *pmin2 = PadLeft(pmin);
char *pmax2 = PadLeft(pmax);
if (strcmp(pnumber2, pmin2) >= 0 && strcmp(pnumber2, pmax2) <= 0)
{
printf(" ** FOUND **\n");
free(pmin2);
free(pmax2);
break;
}
printf(" ** NO GOOD **\n");
free(pmin2);
free(pmax2);
}
fclose(file);
free(pnumber2);
}

Resources