This question already has answers here:
Reading string from input with space character? [duplicate]
(13 answers)
Closed last year.
I'm trying to practice some stuff about text files, printing and reading from them. I need to take input from user -maybe their phone number or someone else's- but i want them to be able to use spaces between numbers
Lets say my phone number is: 565 856 12
i want them to be able to give me this number with spaces, instead of a squished version like 56585612
So far i've tried scanf() and i don't know how to make scanf() do something like this. I've tried going for chars and for loops but its a tangle.
And when i type 565 856 12 and press enter, only 565 will be counted for the phone number. and 856 12 goes for the next scanf.
struct Student{
unsigned long long student_phone_number;
}
int main(){
FILE *filePtr;
filePtr = fopen("std_info.txt","w");
struct Student Student1;
printf("\nEnter Student's Phone Number: ");
scanf("%llu",&Student1.student_phone_number);
fprintf(filePtr,"%llu\t",Student1.student_phone_number);
}
To solve this problem, I modified the Student structure to store both unsigned long long integers and character array. User reads character array from stdin. The read data is validated using the isValid() method, and the string is converted to an unsigned long long integer using the convertToNumber() method.
#include <stdio.h>
#include <ctype.h>
#include <stdbool.h>
struct Student{
unsigned long long numberForm;
char *textForm;
};
// Converts character array to unsigned long long integer.
void convertToNumber(struct Student * const student);
// Validates the data in the Student.textForm variable.
bool isValid(const char * const input, const char * const format);
// Returns the number of characters in a character array.
size_t getSize(const char * const input);
// This function returns the "base ^ exponent" result.
unsigned long long power(int base, unsigned int exponent);
int main()
{
struct Student student;
char format[] = "NNN NNN NN"; /* "123 456 78" */
printf("Enter phone number: ");
fgets(student.textForm, getSize(format) + 1, stdin);
// The gets() function is deprecated in newer versions of the C/C++ standards.
if(isValid(student.textForm, format))
{
convertToNumber(&student);
printf("Result: %llu", student.numberForm);
}
return 0;
}
void convertToNumber(struct Student * const student)
{
int size = getSize(student->textForm) - 2;
unsigned int temp[size];
student->numberForm = 0ull;
for(int i = 0, j = 0 ; i < getSize(student->textForm) ; ++i)
if(isdigit(student->textForm[i]))
temp[j++] = student->textForm[i] - '0';
for(size_t i = 0 ; i < size ; ++i)
student->numberForm += temp[i] * power(10, size - i - 1);
}
bool isValid(const char * const input, const char * const format)
{
if(getSize(input) == getSize(format))
{
size_t i;
for(i = 0 ; i < getSize(input) ; ++i)
{
if(format[i] == 'N')
{
if(!isdigit(input[i]))
break;
}
else if(format[i] == ' ')
{
if(input[i] != format[i])
break;
}
}
if(i == getSize(input))
return true;
}
return false;
}
unsigned long long power(int base, unsigned int exponent)
{
unsigned long long result = 1;
for(size_t i = 0 ; i < exponent ; ++i)
result *= base;
return result;
}
size_t getSize(const char * const input)
{
size_t size = 0;
while(input[++size] != '\0');
return size;
}
This program works as follows:
Enter phone number: 123 465 78
Result: 12346578
You can use fgets to parse an input with spaces and all:
#include <stdio.h>
#define SIZE 100
int main() {
char str[SIZE];
fgets(str, sizeof str, stdin); // parses input string with spaces
// and checks destination buffer bounds
}
If you then want to remove the spaces you can do that easily:
#include <ctype.h>
void remove_white_spaces(char *str)
{
int i = 0, j = 0;
while (str[i])
{
if (!isspace(str[i]))
str[j++] = str[i];
i++;
}
str[j] = '\0';
}
Presto, this function will remove white spaces from the string you pass as an argument.
Live demo
Input:
12 4 345 789
Output:
124345789
After that it's easy to convert this into an unsigned integral value, you can use strtoul, but why would you store a phone number in a numeric type, would you be performing arithmetic operations on it? Doubtfully. And you then save to a file, so it really doesn't matter if it's a string or numeric type. I would just keep it as a string.
It is generally better to store a telephone number as a string, not as a number.
In order to read a whole line of input (not just a single word) as a string, I recommend that you use the function fgets.
Here is an example based on the code in your question:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Student{
char phone_number[50];
};
int main( void )
{
//open output file (copied from code in OP's question)
FILE *filePtr;
filePtr = fopen("std_info.txt","w");
//other variable declarations
struct Student Student1;
char *p;
//prompt user for input
printf( "Enter student's phone number: ");
//attempt to read one line of input
if ( fgets( Student1.phone_number, sizeof Student1.phone_number, stdin ) == NULL )
{
printf( "input error!\n" );
exit( EXIT_FAILURE );
}
//attempt to find newline character, in order to verify that
//the entire line was read in
p = strchr( Student1.phone_number, '\n' );
if ( p == NULL )
{
printf( "line too long for input!\n" );
exit( EXIT_FAILURE );
}
//remove newline character by overwriting it with terminating
//null character
*p = '\0';
//write phone number to file
fprintf( filePtr, "%s\n", Student1.phone_number );
//cleanup
fclose( filePtr );
}
Use string for that purpose and trim the spaces from string and then convert the given number into integer using atoi function, to use atoi you must include stdlib.h header file. For example
#include<stdio.h>
#include<stdlib.h>
int main(){
char str[] = "123456";
unsigned long long int num = atoi(str);
printf("%llu", num);
}
Related
I am making a language translator, and want to read from the buffer word by word and store them in a key-value struct.
The buffer contains such a file:
hola:hello
que:what
and so on. I already tried everything and I keep errors such as segmentation fault: 11 or just reading the same line again and again.
struct key_value{
char *key;
char *value;
};
...
struct key_value *kv = malloc(sizeof(struct key_value) * count);
char k[20]; //key
char v[20]; //value
int x = 0;
for(i = 0; i < numbytes; i++){
sscanf(buffer,"%21[^:]:%21[^\n]\n",k,v);
(kv + i)->key = k;
(kv + i)->value = v;
}
for(i = 0; i < count; i++){
printf("key: %s, value: %s\n",(kv + i)->key,(kv + i)->value);
}
free(buffer);
free(kv);
I expect the output to be key: hola, value: hello key: que, value: what,
but the actual output is just key: hola, value: hello again and again.
Which is the right way to do it?
There are multiple problems with your code, among them
On each loop iteration, you read from the beginning of the buffer. It is natural, then, that each iteration extracts the same key and value.
More generally, your read loop iteration variable seems to have no relationship with the data read. It appears to be a per-byte iteration, but you seem to want a per-line iteration. You might want to look into scanf's %n directive to help you track progress through the buffer.
You are scanning each key / value pair into the same local k and v variables, then you are assigning pointers to those variables to your structures. The resulting pointers are all the same, and they will become invalid when the function returns. I suggest giving structkey_value` arrays for its members instead of pointers, and copying the data into them.
Your sscanf format reads up to 21 characters each for key and value, but the provided destination arrays are not long enough for that. You need them to be dimensioned for at least 22 characters to hold 21 plus a string terminator.
Your sscanf() format and usage do not support recognition of malformed input, especially overlength keys or values. You need to check the return value, and you probably need to match the trailing newline with a %c field (the literal newline in the format does not mean what you think it means).
Tokenizing (the whole buffer) with strtok_r or strtok or even strchr instead of sscanf() might be easier for you.
Also, style note: your expressions of the form (kv + i)->key are valid, but it would be more idiomatic to write kv[i].key.
I've written a simple piece of code that may help you to solve your problem. I've used the function fgets to read from a file named "file.txt" and the function strchr to individuate the 1st occurence of the separator ':'.
Here the code:
#include <stdio.h>
#include <string.h>
#include <errno.h>
#define MAX_LINE_SIZE 256
#define MAX_DECODED_LINE 1024
struct decod {
char key[MAX_LINE_SIZE];
char value[MAX_DECODED_LINE];
};
static struct decod decod[1024];
int main(void)
{
FILE * fptr = NULL;
char fbuf[MAX_LINE_SIZE];
char * value;
int cnt=0,i;
if ( !(fptr=fopen("file.txt","r")) )
{
perror("");
return errno;
}
while( fgets(fbuf,MAX_LINE_SIZE,fptr)) {
// Eliminate UNIX/DOS line terminator
value=strrchr(fbuf,'\n');
if (value) *value=0;
value=strrchr(fbuf,'\r');
if (value) *value=0;
//Find first occurrence of the separator ':'
value=strchr(fbuf,':');
if (value) {
// Truncates fbuf string to first word
// and (++) points second word
*value++=0;
}
if (cnt<MAX_DECODED_LINE) {
strcpy(decod[cnt].key,fbuf);
if (value!=NULL) {
strcpy(decod[cnt].value,value);
} else {
decod[cnt].value[0]=0;
}
cnt++;
} else {
fprintf(stderr,
"Cannot read more than %d lines\n", MAX_DECODED_LINE);
break;
}
}
if (fptr)
fclose(fptr);
for(i=0;i<cnt;i++) {
printf("key:%s\tvalue:%s\n",decod[i].key,decod[i].value);
}
return 0;
}
This code reads all the lines (max 1024) that the file named file.txt contains, loads all individuated couples (max 1024) into the struct array decod and then printouts the content of the structure.
I wrote this code, I think it does the job! this is simpler than the accepted answer I think! and it uses just as much as memory is needed, no more.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct key_value{
char key[22];
char value[22];
};
void parse_str(char* str, struct key_value** kv_arr, int* num){
int n = 0;
int read = -1;
char k[22];
char v[22];
int current_pos = 0;
int consumed = 0;
/*counting number of key-value pairs*/
while (1){
if(current_pos > strlen(str)){
break;
}
read = sscanf(str + current_pos, "%21[^:]:%21[^\n]\n%n", k, v, &consumed);
current_pos += consumed;
if(read == 2){
++n;
}
}
printf("n = %d\n", n);
*kv_arr = malloc(sizeof(struct key_value) * n);
/*filling key_value array*/
int i = 0;
read = -1;
current_pos = 0;
consumed = 0;
while (1){
if(current_pos > strlen(str)){
break;
}
read = sscanf(str + current_pos, "%21[^:]:%21[^\n]\n%n", k, v, &consumed);
current_pos += consumed;
if(read == 2){
struct key_value* kv = &((*kv_arr)[i]);
strncpy(kv->key, k, 22);
strncpy(kv->value, v, 22);
++i;
}
}
*num = n;
}
int main(){
char* str = "hola:hello\n"
"que:what\n";
int n;
struct key_value* kv_arr;
parse_str(str, &kv_arr, &n);
for (int i = 0; i < n; ++i) {
printf("%s <---> %s\n", kv_arr[i].key, kv_arr[i].value);
}
free(kv_arr);
return 0;
}
output :
n = 2
hola <---> hello
que <---> what
Process finished with exit code 0
Note: sscanf operates on a const char*, not an input stream from a file, so it will NOT store any information about what it has consumed.
solution : I used %n in the format string to get the number of characters that it has consumed so far (C89 standard).
I have a file containing a list of numbers separated by commas. I tried different methods of reading data, and this piece of code has worked without issues on different datasets.
Input for example (600 values): https://pastebin.com/AHJ5UpEu
#include <stdio.h>
#include <stdint.h>
#include <malloc.h>
#include <mem.h>
#define READ "r"
#define MAX_LINE_SIZE 4096
#define DATA_DELIMITER ","
unsigned char *readInput(const char *filename, size_t inputs) {
unsigned char *input = malloc(sizeof(unsigned char) * inputs);
unsigned char nbr;
const char *token;
int i;
FILE *inputPtr = fopen(filename, READ);
char line[MAX_LINE_SIZE];
while (fgets(line, MAX_LINE_SIZE, inputPtr)) {
nbr = 0;
for (token = strtok(line, DATA_DELIMITER); token && *token; token = strtok(NULL, ",\n")) {
input[nbr] = (unsigned char) atoi(token);
nbr++;
}
break;
}
fclose(inputPtr);
if(nbr != inputs){
printf("Error, did not read all files. Only read %d\n",nbr);
exit(-1);
}
exit(0);
}
int main() {
unsigned char *d = readInput("../traces/inputs.dat", 600);
free(d);
exit(0);
}
Though it only reads the first 88 values. If I change the max-line-size to for example 512, this number is 145.
Though the value should - if I understand this correct - be equal to the length of the line, in my case ~2100 characters. So using 4098 shouldn't be an issue.
Please do correct me if I'm wrong.
How come I'm not reading all 600 values, but only parts of the data?
nbr is being used like an integer counter but is defined as an unsigned char. A char is one byte, and an unsigned byte has a range of 0 to 255. Incrementing beyond 255 will cause the byte to overflow and return to a value of 0. So, currently, nbr is actually the total number of entries processed mod 256.
I am trying to create a char array based on a single ASCII code. The folowing code does not compile correctly, even though "num" is cast to a char:
//Returns the ASCII counterpart of a number, such as 41 = A, 42 = B, 43 = C, etc.
char numToASCII(int num) {
char[] string = {(char)num, "\0"};
return string;
}
For the task that I am given, it is very important that "string" be a character array/string and not a single char. Any help would be appreciated.
The array must be initialized to constant expressions and your function should return a pointer if you want to return an array.
If you just want to return a char, then use the following code instead:
char numToASCII(int num) {
return (char)num;
}
If you want to return a string which contains the character, then you should use the following code:
#include <stdlib.h>
char *numToASCII(int num) {
/*
* Use malloc to allocate an array in the heap, instead of using a
* local array. The memory space of local array will be freed after
* the invocation of numToASCII.
*/
char *string = malloc(2);
if (!string)
return 0;
string[0] = num;
string[1] = 0;
return string;
}
Use the free() function to free the space allocated by malloc().
Try this..
You want to find the character for the ASCII code,then try this code:
#include<stdio.h>
int main()
{
int num;
printf("\nEnter ASCII Code Number:\t");
scanf("%d", &num);
printf("\nASCII Value of %d: \t%c", num, num);
printf("\n");
return 0;
}
In this code it will get the ASCII code from the user and it will print the character for the ASCII code as default.
Not sure if this helps but pulling text from a file comes back as ascii, I needed a string and got around it by checking the string length, sorry for extra steps as I too am very new.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
FILE *fp;
char firstbuff[yourchoice];
char secondbuff[yourchoice];
char sentence[yourchoice];
int stringlenght;
fp = fopen("test.txt", "r");
//Here add a means of counting the lines in the file as linecount
for(int j = 0; j < linecount; j++)
{
fgets(firstbuff; 1000; fp);
//get string length and use for loop to individually ascii copy as characters into array
stringlength = strlen(firstbuff);
for(int i = 0; i < stringlength; i++)
{
secondbuff[i] = (char)firstbuff[i];
}
//string concat
strcat(sentence, secondbuff);
}
printf("%s\n", sentence);
fclose(fp);
}
Suppose I have a street address stored as a char[]. Examples of valid values:
1600 Pennsylvania Ave
1 Infinite Loop
221 Baker Street
As you can see, the house number can be of any length.
In C, what is an efficient way of separating the house number into its own int? I'm thinking I need to write a while loop to check for each char c if isdigit(c), but I don't know if I'm on the right track here in terms of implementation.
You can use strtok to break up the string into tokens and use isdigit() to figure out if that token is numeric.
#include <string.h>
#include <stdio.h>
int main()
{
const char str1[80] = "1600 Pennsylvania Ave";
int houseNumber = 0;
const char s[2] = " ";
char *token;
/* get the first token */
token = strtok(str, s);
/* walk through other tokens */
while( token != NULL ) {
printf( " %s\n", token );
if (isdigit(str1[0])) {
houseNumber = atoi (token);
}
token = strtok(NULL, s);
}
return(0);
}
Alternatively, you can use sscanf to read the entire string and automatically parse everything:
#include <string.h>
#include <stdio.h>
int main()
{
const char str1[80] = "1600 Pennsylvania Ave";
int houseNumber = 0;
char streetName[50];
char streetExt[20];
sscanf (str1,"%d %s %s", &houseNumber, streetName, streetExt);
return(0);
}
This last method depends on the format of the strings being exactly the same in all cases, meaning that it's always a number followed by 2 strings. The strtok method will be a little more fault tolerant if there's more other stuff.
Consider the following solution:
#include <stdio.h>
#include <string.h>
int main(void)
{
char address[255];
char * ptr;
int number;
char strnumber[100];
char strstreet[255];
// ask adress
printf("Please, enter the addres: ");
// put end of string replacing newline
fgets(address, 255, stdin);
ptr = strchr(address, '\n');
if(ptr)
{
*ptr = '\0';
}
else
{
address[254] = '\0';
}
// try to read whole number from the beggining of the string
if( 1 == sscanf(address, "%d", &number) )
{
// make number as a string (if it is needed)
sprintf(strnumber, "%d", number);
// take streat to separate string
ptr = strchr(address + strlen(strnumber), ' '); // find the firs space
if( ptr )
{
strcpy(strstreet, ptr + 1); // +1 just to skip the found space
}
}
else
{ // if no number at the beginning of address string
number = 0;
strnumber[0] = '\0';
strstreet[0] = '\0';
}
// show the results
printf("You have entered a string:\n%s\n", address);
printf("The found number is:\n%d\n", number);
printf("The found number as a string:\n%s\n", strnumber);
printf("The address without number is:\n%s\n", strstreet);
}
I would use strstr and atoi like this
char someAddress[80] = "1600 Pennsylvania Ave";
char* p = strstr(someAddress, " ");
if (p)
{
*p = 0; // Terminate string, i.e. cheat for a moment
int number = atoi(someAddress);
*p = " "; // Restore someAddress
}
else
{
// Handle illegal format in someAddress
}
This method shall only be used when you know it is safe to modify someAddress for a short period.
This is one situation with the numbers first, where it is much easier just to use a simple pointer and an if statement to parse the line:
#include <stdio.h>
#define ADDL 64
int main (void) {
char address[ADDL] = {0};
char street[16] = {0};
while (fgets (address, ADDL-1, stdin) != NULL)
{
char *ap = address;
char *sp = street;
while (*ap >= '0' && *ap <= '9') /* while the char is a number */
{
*sp = *ap++; /* copy to street number */
sp++;
}
*sp = 0; /* null-terminate */
printf (" Address: %s Number : %s\n\n", address, street);
}
return 0;
}
Output
$ ./bin/split_address < dat/houses.txt
Address: 1600 Pennsylvania Ave
Number : 1600
Address: 1 Infinite Loop
Number : 1
Address: 221 Baker Street
Number : 221
Note: the printf statement above makes use to the embedded newline at the end of address rather than stripping it as you would normally do. Also note that if you want the number as an integer, long, or unsigned, simply call atoi, strtol or strtoul on street.
I can have strings containing random 10 digit numbers e.g.
"abcgfg1234567890gfggf" or
"fgfghgh3215556890ddf" etc
basically any combination of 10 digits plus chars together in a string, so I need check the string to determine if a 10 digit number is present. I use strspn but it returns 0
char str_in[] = "abcgfg1234567890gfggf";
char cset[] = "1234567890";
int result;
result = strspn(str_in, cset); // returns 0 need it to return 10
The fact that the following code returns 0 instead of 10 highlights the problem. I asked this previously but most replies were for checking against a known 10 digit number. In my case the number will be random. Any better way than strspn?
It returns 0 because there are no digits at the start of the string.
The strspn() function calculates the length (in bytes) of the
initial segment of s which consists entirely of bytes in accept.
You need to skip non-digits - strcspn - and then call strspn on the string + that offset. You could try:
/* Count chars to skip. */
skip = strcspn(str_in, cset);
/* Measure all-digit portion. */
length = strspn(str_in + skip, cset)
EDIT
I should mention this must be done in a loop. For example if your string is "abcd123abcd1234567890" the first strspn will only match 3 characters and you need to look further.
Just use sscanf():
unsigned long long value;
const char *str_in = "abcgfg1234567890gfggf";
if(sscanf(str_in, "%*[^0-9]%uL", &value) == 1)
{
if(value >= 1000000000ull) /* Check that it's 10 digits. */
{
/* do magic here */
}
}
The above assumes that unsigned long long is large enough to hold a 10-digit decimal numbers, in practice this means it assumes that's a 64-bit type.
The %*[^0-9] conversion specifier tells sscanf() to ignore a bunch of initial characters that are not (decimal) digits, then convert an unsigned long long (%uL) directly after that. The trailing characters are ignored.
How about using a regex?
#include <stdio.h>
#include <stdlib.h>
#include <regex.h>
int
main(int argc, char **argv)
{
char str_in[] = "abcgfg1234567890gfggf";
int result = 0;
const char *pattern = "[0-9]{10}";
regex_t re;
char msg[256];
if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB) != 0) {
perror("regcomp");
return(EXIT_FAILURE);
}
result = regexec(&re, str_in, (size_t)0, NULL, 0);
regfree(&re);
if (!result) {
printf("Regex got a match.\n");
} else if (result == REG_NOMATCH) {
printf("Regex got no match.\n");
} else {
regerror(result, &re, msg, sizeof(msg));
fprintf(stderr, "Regex match failed: %s\n", msg);
return(EXIT_FAILURE);
}
return(EXIT_SUCCESS);
}
strspn seems handy for this, but you would have to include it in a loop and search several times. Given the specific requirements, the easiest way is probably to make your own custom function.
int find_digits (const char* str, int n);
/* Searches [str] for a sequence of [n] adjacent digits.
Returns the index of the first valid substring containing such a sequence,
otherwise returns -1.
*/
#include <ctype.h>
int find_digits (const char* str, int n)
{
int result = -1;
int substr_len = 0;
int i = 0;
for(int i=0; str[i] != '\0'; i++)
{
if(isdigit(str[i]))
{
substr_len++;
}
else
{
substr_len=0;
}
if(substr_len == n)
{
result = i;
break;
}
}
return result;
}
(I just hacked this down here and now, not tested, but you get the idea. This is most likely the fastest algorithm for the task, that is, if performance matters at all)
Alternative use of sscanf()
(blatant variation of #unwind)
const char *str_in = "abcgfg0123456789gfggf";
int n1 = 0;
int n2 = 0;
// %*[^0-9] Scan any non-digits. Do not store result.
// %n Store number of characters read so far.
// %*[0-9] Scan digits. Do not store result.
sscanf(str_in, "%*[^0-9]%n%*[0-9]%n", &n1, &n2);
if (n2 == 0) return 0;
return n2 - n1;
Counts leading 0 characters as part of digit count.
Should one wish to avoid sscanf()
char str_in[] = "abcgfg1234567890gfggf";
const char *p1 = str_in;
while (*p1 && !isdigit(*p1)) p1++;
const char *p2 = p1;
while (isdigit(*p2)) p2++;
result = p2 - p1;
for testing a suit of "0123456789" inside a string you can do something like that:
int main()
{
char str_in[] = "abcgfg1234567890gfggf";
char cset[] = "1234567890";
int result;
int i;
int f;
i = 0;
f = 0;
while (str_in[i])
{
if (str_in[i] == cset[f])
{
f++;
if(f == strlen(cset))
return (f);
}
else
f = 0;
i++;
}
}