My problem is when I try to save the string (series[0]) Inside (c[0])
and I display it, it always ignore the last digit.
For Example the value of (series[0]) = "1-620"
So I save this value inside (c[0])
and ask the program to display (c[0]), it displays "1-62" and ignores the last digit which is "0". How can I solve this?
This is my code:
#include <stdio.h>
int main(void)
{
int price[20],i=0,comic,j=0;
char name,id,book[20],els[20],*series[20],*c[20];
FILE *rent= fopen("read.txt","r");
while(!feof(rent))
{
fscanf(rent,"%s%s%s%d",&book[i],&els[i],&series[i],&price[i]);
printf("1.%s %s %s %d",&book[i],&els[i],&series[i],price[i]);
i++;
}
c[0]=series[0];
printf("\n%s",&c[0]);
return 0;
}
The use of fscanf and printf is wrong :
fscanf(rent,"%s%s%s%d",&book[i],&els[i],&series[i],&price[i]);
Should be:
fscanf(rent,"%c%c%s%d",&book[i],&els[i],series[i],&price[i]);
You have used the reference operator on a char pointer when scanf expecting a char pointer, also you read a string to book and else instead of one character.
printf("1.%s %s %s %d",&book[i],&els[i],&series[i],price[i]);
Should be:
printf("1.%c %c %s %d",book[i],els[i],series[i],price[i]);
And:
printf("\n%s",&c[0]);
Should be:
printf("\n%s",c[0]);
c is an array of char * so c[i] can point to a string and that is what you want to send to printf function.
*Keep in mind that you have to allocate (using malloc) a place in memory for all the strings you read before sending them to scanf:
e.g:
c[0] = (char*)malloc(sizeof(char)*lengthOfString+1);
and only after this you can read characters in to it.
or you can use a fixed size double character array:
c[10][20];
Now c is an array of 20 strings that can be up to 9 characters long.
Amongst other problems, at the end you have:
printf("\n%s",&c[0]);
There are multiple problems there. The serious one is that c[0] is a char *, so you're passing the address of a char * — a char ** — to printf() but the %s format expects a char *. The minor problem is that you should terminate lines of output with newline.
In general, you have a mess with your memory allocation. You haven't allocated space for char *series[20] pointers to point at, so you get undefined behaviour when you use it.
You need to make sure you've allocated enough space to store the data, and it is fairly clear that you have not done that. One minor difficulty is working out what the data looks like, but it seems to be a series of lines each with 3 words and 1 number. This code does that job a bit more reliably:
#include <stdio.h>
int main(void)
{
int price[20];
int i;
char book[20][32];
char els[20][32];
char series[20][20];
const char filename[] = "read.txt";
FILE *rent = fopen(filename, "r");
if (rent == 0)
{
fprintf(stderr, "Failed to open file '%s' for reading\n", filename);
return 1;
}
for (i = 0; i < 20; i++)
{
if (fscanf(rent, "%31s%31s%19s%d", book[i], els[i], series[i], &price[i]) != 4)
break;
printf("%d. %s %s %s %d\n", i, book[i], els[i], series[i], price[i]);
}
printf("%d titles read\n", i);
fclose(rent);
return 0;
}
There are endless ways this could be tweaked, but as written, it ensures no overflow of the buffers (by the counting loop and input conversion specifications including the length), detects when there is an I/O problem or EOF, and prints data with newlines at the end of the line. It checks and reports if it fails to open the file (including the name of the file — very important when the name isn't hard-coded and a good idea even when it is), and closes the file before exiting.
Since you didn't provide any data, I created some random data:
Tixrpsywuqpgdyc Yeiasuldknhxkghfpgvl 1-967 8944
Guxmuvtadlggwjvpwqpu Sosnaqwvrbvud 1-595 3536
Supdaltswctxrbaodmerben Oedxjwnwxlcvpwgwfiopmpavseirb 1-220 9698
Hujpaffaocnr Teagmuethvinxxvs 1-917 9742
Daojgyzfjwzvqjrpgp Vigudvipdlbjkqjm 1-424 4206
Sebuhzgsqpyidpquzjxswbccqbruqf Vuhssjvcjjylcevcisdzedkzlp 1-581 3451
Doeraxdmyqcbbzyp Litbetmttcgfldbhqqfdxqi 1-221 2485
Raqqctfdlhrmhtzusntvgbvotpk Iowdcqlwgljwlfvwhfmw 1-367 3505
Kooqkvabwemxoocjfaa Hicgkztiqvqdjjx 1-466 435
Lowywyzzkkrazfyjuggidsqfvzzqb Qiginniroivqymgseushahzlrywe 1-704 5514
The output from the code above on that data is:
0. Tixrpsywuqpgdyc Yeiasuldknhxkghfpgvl 1-967 8944
1. Guxmuvtadlggwjvpwqpu Sosnaqwvrbvud 1-595 3536
2. Supdaltswctxrbaodmerben Oedxjwnwxlcvpwgwfiopmpavseirb 1-220 9698
3. Hujpaffaocnr Teagmuethvinxxvs 1-917 9742
4. Daojgyzfjwzvqjrpgp Vigudvipdlbjkqjm 1-424 4206
5. Sebuhzgsqpyidpquzjxswbccqbruqf Vuhssjvcjjylcevcisdzedkzlp 1-581 3451
6. Doeraxdmyqcbbzyp Litbetmttcgfldbhqqfdxqi 1-221 2485
7. Raqqctfdlhrmhtzusntvgbvotpk Iowdcqlwgljwlfvwhfmw 1-367 3505
8. Kooqkvabwemxoocjfaa Hicgkztiqvqdjjx 1-466 435
9. Lowywyzzkkrazfyjuggidsqfvzzqb Qiginniroivqymgseushahzlrywe 1-704 5514
10 titles read
Related
I'm new to C and I'm trying to write a program that prints the ASCII value for every letter in a name that the user enters. I attempted to store the letters in an array and try to print each ASCII value and letter of the name separately but, for some reason, it only prints the value of the first letter.
For example, if I write "Anna" it just prints 65 and not the values for the other letters in the name. I think it has something to do with my sizeof(name)/sizeof(char) part of the for loop, because when I print it separately, it only prints out 1.
I can't figure out how to fix it:
#include <stdio.h>
int main(){
int e;
char name[] = "";
printf("Enter a name : \n");
scanf("%c",&name);
for(int i = 0; i < (sizeof(name)/sizeof(char)); i++){
e = name[i];
printf("The ASCII value of the letter %c is : %d \n",name[i],e);
}
int n = (sizeof(name)/sizeof(char));
printf("%d", n);
}
Here's a corrected, annotated version:
#include <stdio.h>
#include <string.h>
int main() {
int e;
char name[100] = ""; // Allow for up to 100 characters
printf("Enter a name : \n");
// scanf("%c", &name); // %c reads a single character
scanf("%99s", name); // Use %s to read a string! %99s to limit input size!
// for (int i = 0; i < (sizeof(name) / sizeof(char)); i++) { // sizeof(name) / sizeof(char) is a fixed value!
size_t len = strlen(name); // Use this library function to get string length
for (size_t i = 0; i < len; i++) { // Saves calculating each time!
e = name[i];
printf("The ASCII value of the letter %c is : %d \n", name[i], e);
}
printf("\n Name length = %zu\n", strlen(name)); // Given length!
int n = (sizeof(name) / sizeof(char)); // As noted above, this will be ...
printf("%d", n); // ... a fixed value (100, as it stands).
return 0; // ALWAYS return an integer from main!
}
But also read the comments given in your question!
This is a rather long answer, feel free to skip to the end for the code example.
First of all, by initialising a char array with unspecified length, you are making that array have length 1 (it only contains the empty string). The key issue here is that arrays in C are fixed size, so name will not grow larger.
Second, the format specifier %c causes scanf to only ever read one byte. This means that even if you had made a larger array, you would only be reading one byte to it anyway.
The parameter you're giving to scanf is erroneous, but accidentally works - you're passing a pointer to an array when it expects a pointer to char. It works because the pointer to the array points at the first element of the array. Luckily this is an easy fix, an array of a type can be passed to a function expecting a pointer to that type - it is said to "decay" to a pointer. So you could just pass name instead.
As a result of these two actions, you now have a situation where name is of length 1, and you have read exactly one byte into it. The next issue is sizeof(name)/sizeof(char) - this will always equal 1 in your program. sizeof char is defined to always equal 1, so using it as a divisor causes no effect, and we already know sizeof name is equal to 1. This means your for loop will only ever read one byte from the array. For the exact same reason n is equal to 1. This is not erroneous per se, it's just probably not what you expected.
The solution to this can be done in a couple of ways, but I'll show one. First of all, you don't want to initialize name as you do, because it always creates an array of size 1. Instead you want to manually specify a larger size for the array, for instance 100 bytes (of which the last one will be dedicated to the terminating null byte).
char name[100];
/* You might want to zero out the array too by eg. using memset. It's not
necessary in this case, but arrays are allowed to contain anything unless
and until you replace their contents.
Parameters are target, byte to fill it with, and amount of bytes to fill */
memset(name, 0, sizeof(name));
Second, you don't necessarily want to use scanf at all if you're reading just a byte string from standard input instead of a more complex formatted string. You could eg. use fgets to read an entire line from standard input, though that also includes the newline character, which we'll have to strip.
/* The parameters are target to write to, bytes to write, and file to read from.
fgets writes a null terminator automatically after the string, so we will
read at most sizeof(name) - 1 bytes.
*/
fgets(name, sizeof(name), stdin);
Now you've read the name to memory. But the size of name the array hasn't changed, so if you used the rest of the code as is you would get a lot of messages saying The ASCII value of the letter is : 0. To get the meaningful length of the string, we'll use strlen.
NOTE: strlen is generally unsafe to use on arbitrary strings that might not be properly null-terminated as it will keep reading until it finds a zero byte, but we only get a portable bounds-checked version strnlen_s in C11. In this case we also know that the string is null-terminated, because fgets deals with that.
/* size_t is a large, unsigned integer type big enough to contain the
theoretical maximum size of an object, so size functions often return
size_t.
strlen counts the amount of bytes before the first null (0) byte */
size_t n = strlen(name);
Now that we have the length of the string, we can check if the last byte is the newline character, and remove it if so.
/* Assuming every line ends with a newline, we can simply zero out the last
byte if it's '\n' */
if (name[n - 1] == '\n') {
name[n - 1] = '\0';
/* The string is now 1 byte shorter, because we removed the newline.
We don't need to calculate strlen again, we can just do it manually. */
--n;
}
The loop looks quite similar, as it was mostly fine to begin with. Mostly, we want to avoid issues that can arise from comparing a signed int and an unsigned size_t, so we'll also make i be type size_t.
for (size_t i = 0; i < n; i++) {
int e = name[i];
printf("The ASCII value of the letter %c is : %d \n", name[i], e);
}
Putting it all together, we get
#include <stdio.h>
#include <string.h>
int main() {
char name[100];
memset(name, 0, sizeof(name));
printf("Enter a name : \n");
fgets(name, sizeof(name), stdin);
size_t n = strlen(name);
if (n > 0 && name[n - 1] == '\n') {
name[n - 1] = '\0';
--n;
}
for (size_t i = 0; i < n; i++){
int e = name[i];
printf("The ASCII value of the letter %c is : %d \n", name[i], e);
}
/* To correctly print a size_t, use %zu */
printf("%zu\n", n);
/* In C99 main implicitly returns 0 if you don't add a return value
yourself, but it's a good habit to remember to return from functions. */
return 0;
}
Which should work pretty much as expected.
Additional notes:
This code should be valid C99, but I believe it's not valid C89. If you need to write to the older standard, there are several things you need to do differently. Fortunately, your compiler should warn you about those issues if you tell it which standard you want to use. C99 is probably the default these days, but older code still exists.
It's a bit inflexible to be reading strings into fixed-size buffers like this, so in a real situation you might want to have a way of dynamically increasing the size of the buffer as necessary. This will probably require you to use C's manual memory management functionality like malloc and realloc, which aren't particularly difficult but take greater care to avoid issues like memory leaks.
It's not guaranteed the strings you're reading are in any specific encoding, and C strings aren't really ideal for handling text that isn't encoded in a single-byte encoding. There is support for "wide character strings" but probably more often you'll be handling char strings containing UTF-8 where a single codepoint might be multiple bytes, and might not even represent an individual letter as such. In a more general-purpose program, you should keep this in mind.
If we need write a code to get ASCII values of all elements in a string, then we need to use "%d" instead of "%c". By doing this %d takes the corresponding ascii value of the following character.
If we need to only print the ascii value of each character in the string. Then this code will work:
#include <stdio.h>
char str[100];
int x;
int main(){
scanf("%s",str);
for(x=0;str[x]!='\0';x++){
printf("%d\n",str[x]);
}
}
To store all corresponding ASCII value of character in a new variable, we need to declare an integer variable and assign it to character. By this way the integer variable stores ascii value of character. The code is:
#include <stdio.h>
char str[100];
int x,ascii;
int main(){
scanf("%s",str);
for(x=0;str[x]!='\0';x++){
ascii=str[x];
printf("%d\n",ascii);
}
}
I hope this answer helped you.....😊
I am in school and got an assignment to write a C program that takes an input from a user then scans a file and returns how many times that word shows up in a file. I feel like I got it 90% done, but for some reason I can't get the while loop. When I run the program it crashes at the while loop. Any help or guidance would be greatly appreciated.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <windows.h>
int main() {
char input[50], file[50], word[50];
int wordcount;
printf("Enter a string to search for\n");
scanf("%s", input);
printf("Enter a file location to open\n");
scanf("%s", file);
FILE * fp;
fp = fopen("%s", "r", file);
while (fscanf(fp, "%s", word) != EOF) {
if (strcmp(word, input)) {
printf("found the word %s\n", input);
wordcount++;
}
}
printf("The world %s shows up %d times\n", input, wordcount);
system("pause");
}
You have 2 problems:
fp = fopen("%s", "r", file);
is incorrect, fopen expects only two arguments, not three. The correct version
is
fp = fopen(file, "r");
Note that there is no feature in the C language that allows you to construct
strings from variables like this "%s", variable1. This only works for function
like printf that read a format and interpret the format base on a fix set of
rules you can see here.
The second problem is this:
if (strcmp(word, input))
strcmp is used to compared two strings, however it return 0 when the strings
are equal, non-zero otherwise. So the correct check should be
if(strcmp(word, input) == 0)
{
printf("found the word %s\n", input);
wordcount++;
}
One last thing: when you read a string with scanf, you should limit the amount
of characters to be read, otherwise you will overflow the buffer and this yield
undefined behaviour which could lead to a segfault.
input is a char[50], so it can hold at most 49 characters, the better
scanf call would be
scanf("%49s", input);
with this you are making sure not to write beyond the bounds of the array.
Fotenotes
1The string "%s" has no real meaning in the C language, like any
other string it is merly a sequence of characters that ends with the
'\0'-terminating character. The memory layout for this strings is
+---+---+----+
| % | s | \0 |
+---+---+----+
The printf family of functions however give certains sequences of characters
(the ones beginning with %) a well defined meaning. They're used to determine the type of the variable that should
be used when printing as well as other format options. See the printf documentation for more information about that. You have to
remember however, that this type of constructs only works with printf because
printf was design to work this way.
If you need to construct a string using values of other variables, then you need
to have an array with enough space and use a function like sprintf. For
example:
const char *base = "records";
int series = 8;
char fn[100];
sprintf(fn, "%s%d.dat", base, series);
// now fn has the string "records8.dat"
FILE *fp = fopen(fn, "r");
...
But in your case this is unnecessary because the whole filename was already
stored in variable file, so construction a new string based on file is not
needed.
You are trying to open a file named "%s", which I'm pretty sure does not exist. If you had checked the return from fopen, you could have figured it out yourself.
I would like to parse a specific line. So, I wrote the following piece of code in order to test the logic but I probably understand something wrongly :
typedef struct vers
{
char tu8UVersion[5];
char tu8UCommit[32];
}tst_prg_versions;
int main(int argc, char **argv)
{
tst_prg_versions lstVer;
char buf1[32];
char buf2[32];
char str[] = "BOARD-VERS-v1.0.0-git+9abc12345a";
sscanf(str, "BOARD-VERS-v%5s-git+%s", lstVer.tu8UVersion, lstVer.tu8UCommit);
printf("vers='%s'\n", lstVer.tu8UVersion);
printf("commit='%s'\n", lstVer.tu8UCommit);
sscanf(str, "BOARD-VERS-v%5s-git+%s", buf1, buf2);
printf("vers='%s'\n", buf1);
printf("commit='%s'\n", buf2);
return 0;
}
Once executed it returns :
vers='1.0.09abc12345a'
commit='9abc12345a'
vers='1.0.0'
commit='9abc12345a
Why the first vers is equals to 1.0.09abc12345a and not 1.0.0 ?
The first actually reads 1.0.0! Problem is, however, that tu8UVersion is not null-terminated, thus printf (not sscanf) prints beyound the field (doing so is undefined behaviour, however, as noted by sjsam) - which is immediately followed by tu8UCommit (does not necessarily have to be so, there could still be some fill bytes in between for alignment reasons!).
You need to either print 5 characters at most (%.5s in printf format string) or leave place for terminating the tu8UVersion with 0, as proposed in a comment already.
Something similar could have happened with your buffers, too. You are lucky that they appearently have been initialized to 0 already (probably because of compiled as debug version), which again does not necessarily have to happen. So with bad luck, you could have printed the whole rest of buf1 (having been left at garbage) and even beyond.
Why the first vers is equals to 1.0.09abc12345a and not 1.0.0 ?
Remember that you have
typedef struct vers
{
char tu8UVersion[5];
char tu8UCommit[32];
}tst_prg_versions;
I guess, there is a good chance the memory for tu8UVersion and tu8UCommit is contiguous. Since you have not null-terminated tu8UVersion when you do :
printf("vers='%s'\n", lstVer.tu8UVersion);
it goes on to print tu8UCommit and it stops because tu8UCommit is null terminated.
While sscanf seem the most sensible solution here you could also introduce some formatting :
char tu8UVersion[32];
/* version number can't get too big.
* So the first step is do allocated a
* reasonably - but not too - big size for it.
* So that you can be sure there are few empty bytes at the end.
*/
and then use a function to sanitize a string :
char* sanitized(char* ptr)
{
if(ptr[strlen(ptr)]!='\0') // include string.h for strlen
ptr[strlen(ptr)]='\0';
return ptr;
}
and print it like :
printf("vers='%s'\n", sanitized(lstVer.tu8UVersion));
Your problem has already been identified in the comments: You don't leave space for the terminating null character and the two strings are run together.
If you want to scan a version whose size you don't know beforehand, you can limit the characters to scan to decimal digits and points with %[.-9] or to everything except a hyphen with %[^-]. (The %[...] format is like %s, except that you must provide a list of valid characters in the brackets. A caret as first letter means that the string is made up of characters that are not listed. In other words, %s is short for %[^ \t\n]
When you scan a string, you should test the return value of sscanf to be sure that all items have been scanned correctly and contain valid values.
Here's a variant that scans version numbers of up to 11 letters:
#include <stdlib.h>
#include <stdio.h>
typedef struct vers
{
char tu8UVersion[12];
char tu8UCommit[32];
} tst_prg_versions;
int main(int argc, char **argv)
{
tst_prg_versions lstVer;
char str[] = "BOARD-VERS-v1.0.0-git+9abc12345a";
int n;
n = sscanf(str, "BOARD-VERS-v%11[^-]-git+%s",
lstVer.tu8UVersion, lstVer.tu8UCommit);
if (n == 2) {
printf("vers='%s'\n", lstVer.tu8UVersion);
printf("commit='%s'\n", lstVer.tu8UCommit);
} else {
puts("Parse error.");
}
return 0;
}
I have a file called pi.txt which contains, as you probably imagine, the numbers of pi.
The format goes like this
Line 1: 3.
Line 2: 14159265358979323846264338327950288419716939937510
Line 3: 58209749445923078164062862089986280348253421170679
and it continues to
Line 20001: 56787961303311646283996346460422090106105779458151
There are 1020001 digits minus the 3 and the .(of 3.14xxxxxx...)
I have to read this file to an array (not a 2D one. What I want to do later will become much harder if I use a 2D array as I imagine it). This is my code:
void fpi();
char **arraypi;
int main(int argc, char *argv[]) {
fpi();
int i;
while (i<=10){
printf("%d", arraypi[i++]);}
return 0;
}
void fpi(){
char pi[1020001];
arraypi = malloc(1020001 * sizeof(int));
FILE *file;
int i=0;
file = fopen("pi.txt", "r");
if (file == NULL){
printf("The file cannot be opened.\n");
exit(1);
}
while (fscanf(file, "%c", &pi)==1){
strcpy(arraypi[i++], pi);
}
fclose(file);
}
I get a segmentation fault and I can't figure out why. I'm sure it has to do with the pointers I'm using and fscanf.
strcpy expects you to give a null terminated character array (string) and you're giving an array of 1020001 characters where except the first character, which is read from the file, everything else is undetermined.
char pi[1020001];
while (fscanf(file, "%c", &pi)==1){ // only one character gets copied to pi
strcpy(arraypi[i++], pi); // strcpy will expect pi to be a string and not just a char
}
Hence it may go on an infinite loop and write beyond the allowed memory allocated for it, thereby corrupting the stack structure and thus leading to the segmentation fault. You're also treading on undefined behaviour when you're doing this.
I am having problems reading strings with sscanf. I have dumbed down the code to focus on the problem. Below is a function in the whole code that is supposed to open a file and read something. But sscanf is acting strangely. For instance I declare a string called atm with the content 'ATOM'. Before the sscanf it prints this string as ATOM while after it is null. What could be the problem? I assume it must be an allocation problem but I could not find it. I tried some suggestions on other topics like replacing %s with other things but it did not help.
void Get (struct protein p, int mode, int type)
{
FILE *fd; //input file
char name[100]="1CMA"; //array for input file name
char string[600]; //the array where each line of the data file is stored when reading
char atm[100]="ATOM";
char begin[4];
int index1 =0;
fd = fopen(name, "r"); // open the input file
if(fd==NULL) {
printf("Error: can't open file.\n");
return 1;
}
if( type==0 ) { //pdb file type
if( mode==0 ) {
while( fgets(string, 600, fd)!=NULL ) {
printf("1 %s\n",atm);
sscanf (string, "%4s", begin );
printf("2 %s \n",atm);
}
}
}
fclose(fd);
free(fd);
free(name);
}
The string begin isn't big enough to hold the four characters that sscanf will read and its \0 terminator. If the \0 is written into atm (depending on where the strings are in memory), atm would be modified. From the sscanf manpage, about the s directive:
s Matches a sequence of non-white-space characters; the next pointer must be a pointer to character array that is long enough to hold the input sequence and the terminating null byte ('\0'), which is added automatically. The input string stops at white space or at the maximum field width, whichever occurs first.
I was able to reproduce this behavior on my machine, though the exact positioning of the strings in memory was a bit different. By printing the addresses of the strings, though, it is easy to figure exactly what's happening. Here's a minimal example:
#include<stdio.h>
int main() {
char begin[2];
char atm[100]="ATOM";
printf("begin: %p\n", begin);
printf("begin+16: %p\n", begin+16);
printf("atom: %p\n", atm);
printf("1 %s\n",atm);
sscanf("AAAABBBBCCCCDDDD", "%16s", begin);
printf("2 %s \n",atm);
return 0;
}
This produces the output:
$ ./a.out
begin: 0x7fffffffe120
begin+16: 0x7fffffffe130
atom: 0x7fffffffe130
1 ATOM
2
I printed the values of the pointers to figure out how big a string it would take to overflow into atm. Since (on my machine) atom begins at begin+16, reading sixteen characters into begin puts a null terminator at begin+16, which is the first character of atm, so now atm has length 0.