Reading file and storing into variable - c

I have spent the last 4 or 5 hours on something that is probably quite simple, I have tried many different methods. What I am trying to do is make my program save the end of each line in a saved test file. This would be the players names followed by the shirt number or weight etc. The file is saved in a format like this:
First name is:xxxxxx
Last name is:xxxxx
Date of birth is:xxxxxx
I want my code to be able to just store the xxxxxx in a separate variable so then i can un-encrypt it as only them parts are encrypted.
The code I have got so far is
int main()
{
int dob;
char lastname[15], *ptr, filename[25], line[40], storage[200], a[15];
FILE *file;
printf("Please enter the last name of your player: ");
scanf("%s", lastname);
printf("\nPlease enter the date of birth of your player: ");
scanf("%d", &dob);
printf("\n\n");
sprintf(filename, "%s%6d.pat", lastname, dob);
printf("%s", filename);
file = fopen(filename, "r");
while(fgets(line, sizeof(line), file) != NULL)
{
fprintf(stdout, "%s", line);
}
fscanf(file, "%s: %s\n", storage, a);
printf("%s %s", storage, a);
I am now currently trying to use this piece of code to the get the last string after the : then apply a small decryption to it, but i seem to get an infinite loop of just the name after the first line.
do
{
if(sscanf(line,"%*[^:]:%19s",s)==1)
{
for(i = 0; i < slen; i++) /*encrypting firstname*/
{
slen = strlen(s);
s[i] = (char)((s[i] - 'a' - 4) % 26 + 'a');
if(s == '\0')
{
break;
}
}
printf("%s",s);
slen = strlen(s);
slen--;
}
}while(slen > 0);

You can make use of sscanf function -
char s[20];
if(sscanf(line,"%*[^:]:%19s",s)==1){ //check return of sscanf
/* %*[^:] will read string till ':' and discard it */
printf("%s",s);
}
This will store your desired string in s.
Note - Data in your file should in form as you show in question.
If you want data even outside loop then use a 2-d char array instead . Increment index of array in loop as you read new data from file.
EDIT-
Dont use uninitialized variable slen before calculating string length . You can re-write your code as -
int i=0;
if(sscanf(line,"%*[^:]:%19s",s)==1)
{
slen = strlen(s);
while(i<slen)/*encrypting firstname*/
{
//slen = strlen(s);
s[i] = (char)((s[i] - 'a' - 4) % 26 + 'a');
if(s == '\0')
{
break;
}
i++;
}
printf("%s",s);
slen--;
}

Related

Reading columns of strings and integers from file

I am able to read chars, words, sentences and integers from separate files but I am struggling to read words and integers from the same file. Let's say my file contains the following:
Patrice 95
Rio 96
Marcus 78
Wayne 69
Alex 67
Chris 100
Nemanja 78
My partial solution (to read in strings) so far was to use fgetc() and check for spaces and or carriage returns in my text file to separate the name from the number.
The main issue with fgetc is that it reads in character by character, and so integers are not meant to be read in like this. As a workaround, I am converting the character to an integer whenever a number is read in.
This is the main code structure:
typedef struct person {
char name[10][10];
char surname[10][10];
int age [10];
} person_t;
FILE *inp; /* pointer to input file */
char c;
int word_count = 0;
int char_count = 0;
int i = 0;
int x;
person_t my_person;
while ((c = fgetc(inp)) != EOF) {
if (c == ' ' || c == '\r') {
printf("\n");
my_person.name[word_count][char_count] = '\0'; //Terminate the string
char_count = 0; //Reset the counter.
word_count++;
}
else {
if (c >= '0' && c <= '9') {
x = c - '0'; //converting to int
my_person.age[i] = x;
printf("%d", my_person.age[i]);
i++;
}
else {
my_person.name[word_count][char_count] = c;
printf("%c",my_person.name[word_count][char_count]);
if (char_count < 19) {
char_count++;
}
else {
char_count = 0;
}
}
}
}
}
for (int i = 0; i<7; i++) {
printf("ages: %d \n",my_person.age[i] ); //never executes
}
Sample Output:
Patrice
95
Rio
96
Marcus
78
Wayne
69
Alex
67
Chris
Full code can be found on pastebin.
Why is the for loop never executing? Any suggestions on what I can improve to read the columns of strings and integers?
Use fgets() to read a whole line.
char line[100];
while (fgets(line, sizeof line, inp)) {
// got a line, need to isolate parts
}
Then, depending on whether the words can have embedded spaces choose one of the strategies below.
a) sscanf() to isolate name and age
while (fgets(line, sizeof line, inp)) {
char name[30];
int age;
if (sscanf(line, "%29s%d", name, &age) != 2) /* error, bad line */;
// ...
}
b) strrchr() to find the last space, then string manipulation to extract name and age.
while (fgets(line, sizeof line, inp)) {
char name[30];
int age;
char *space = strrchr(line, ' ');
if (!space) /* error, bad line */;
if (space - line >= 30) /* error, name too long */;
sprintf(name, "%.*s", space - line, line);
age = strtol(space, NULL, 10); // needs error checking
// ...
}
strategy b) on https://ideone.com/ZOLie9

C extract words from a txt file except spaces and punctuations

I'm trying to extract the words from a .txt file which contains the following sentence
Quando avevo cinqve anni, mia made mi perpeteva sempre che la felicita e la chiave della vita. Quando andai a squola mi domandrono come vuolessi essere da grande. Io scrissi: selice. Mi dissero che non avevo capito il corpito, e io dissi loro che non avevano capito la wita.
The problem is that in the array that I use to store the words, it stores also empty words ' ' which come always after one of the following ',' '.' ':'
I know that things like "empty words" or "empty chars" don't make sense but please try the code with the text that I've passed and you'll understand.
Meanwhile I'm trying to understand the use of sscanf with this modifier sscanf(buffer, "%[^.,:]"); that should allow me to store strings ignoring the . and , and : characters however I don't know what should i write in %[^] to ignore the empty character ' ' which always gets saved.
The code is the following
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
static void load_array(const char* file_name){
char buffer[2048];
char a[100][100];
int buf_size = 2048;
FILE *fp;
int j = 0, c = 0;
printf("\nLoading data from file...\n");
fp = fopen(file_name,"r");
if(fp == NULL){
fprintf(stderr,"main: unable to open the file");
exit(EXIT_FAILURE);
}
fgets(buffer,buf_size,fp);
//here i store each word in an array of strings when I encounter
//an unwanted char I save the word into the next element of the
//array
for(int i = 0; i < strlen(buffer); i++) {
if((buffer[i] >= 'a' && buffer[i] <= 'z') || (buffer[i] >= 'A' && buffer[i] <= 'Z')) {
a[j][c++] = buffer[i];
} else {
j++;
c = 0;
continue;
}
}
//this print is used only to see the words in the array of strings
for(int i = 0; i < 100; i++)
printf("%s %d\n", a[i], i);
fclose(fp);
printf("\nData loaded\n");
}
//Here I pass the file_name from command line
int main(int argc, char const *argv[]) {
if(argc < 2) {
printf("Usage: ordered_array_main <file_name>\n");
exit(EXIT_FAILURE);
}
load_array(argv[1]);
}
I know that I should store only the necessary number and words and not 100 everytime, I want to think about that later on, right now I want to fix the issue with the empty words.
Compilation and execution
gcc -o testloadfile testloadfile.c
./testloadfile "correctme.txt"
you could instead try to use strtok
fgets(buffer,buf_size,fp);
for (char* tok = strtok(buffer,".,: "); *tok; tok = strtok(NULL,".,: "))
{
printf("%s\n", tok);
}
Note that if you want to store what strtok returns you need to either copy the contents of what tok points to or allocate a copy using strdup/malloc+strcpy since strtok modifies its copy of the first argument as it parses the string.
You forgot to add the final '\0' in each of a's line, and your algorithm have many flaw (like how you increment j each time a non-letter appear. What if you have ", " ? you increment two time instead of one).
One "easy" way is to use "strtok", as Anders K. show you.
fgets(buffer,buf_size,fp);
for (char* tok = strtok(buffer,".,:"); *tok; tok = strtok(NULL,".,:")) {
printf("%s\n", tok);
}
The "problem" of that function, is that you have to specify all the delimiter, so you have to add ' ' (space), '\t' (tabulation) etc etc.
Since you only want "word" as described by "contain only letter, minuscule or majuscule", then you can do the following:
int main(void)
{
char line[] = "Hello ! What a beautiful day, isn't it ?";
char *beginWord = NULL;
for (size_t i = 0; line[i]; ++i) {
if (isalpha(line[i])) { // upper or lower letter ==> valid character for a word
if (!beginWord) {
// We found the beginning of a word
beginWord = line + i;
}
} else {
if (beginWord) {
// We found the end of a word
char tmp = line[i];
line[i] = '\0';
printf("'%s'\n", beginWord);
line[i] = tmp;
beginWord = NULL;
}
}
}
return (0);
}
Note that how "isn't" is splitted in "isn" and "t", since ' is not an accpeted character for your word.
The algo is pretty simple: we just loop the string, and if it's a valid letter and beginWord == NULL, then it's the beginning of the word. If it's not a valid letter and beginWord != NULL, then it's the end of a word. Then you can have every number of letter between two word, you still can detect cleanly the word.

How to store strings that end with certain characters?

I want to create a program that opens and writes to 3 different text files, the names a user inputs.
The condition would be that if last names end with certain characters, they would be saved to a specific text file.
For example, if the user inputs a last name that ends with ian it should be saved to the ARMENIA.TXT folder.
Here is my code and the issues I encounter with it:
struct names {
char firstName[20];
char lastName[30];
} person;
int main() {
FILE *arm, *ita, *esp;
char ian[] = "ian";
char ini[] = "ini";
char ez[] = "ez";
char response;
char *ret;
arm = fopen("C:\\Programming\\ARMENIA.TXT", "wt");
ita = fopen("C:\\Programming\\ITALIA.TXT", "wt");
esp = fopen("C:\\Programming\\ESPANIA.TXT", "wt");
if (arm, ita, esp == NULL) {
printf("Error: The archives could not be created.");
return 1;
} else {
printf("Operation Successful: Archives created.\n\n");
}
do {
fflush(stdin);
printf("\n\nPlease input your first name: ");
scanf("%s", &person.firstName);
printf("Please input your last name: ");
scanf("%s", &person.lastName);
if (ret = strstr(person.lastName, ian)) {
fwrite(person.lastName, 1, strlen(person.lastName), arm);
fwrite(person.firstName, 1, strlen(person.firstName), arm);
}
if (ret = strstr(person.lastName, ini)) {
fwrite(person.lastName, 1, strlen(person.lastName), ini);
fwrite(person.firstName, 1, strlen(person.firstName), ini);
}
if (ret = strstr(person.lastName, ez)) {
fwrite(person.lastName, 1, strlen(person.lastName), ez);
fwrite(person.firstName, 1, strlen(person.firstName), ez);
}
printf("\n\nWould you like to enter another person into the archive?: (y) or (n): ");
scanf(" %c", &response);
} while (response == 'y');
printf("\n\nThe operation has finished.\n\n");
fclose(arm);
fclose(ita);
fclose(esp);
return 0;
}
Issue: Will save to folder if last name contains ian (or ini / ez) in ANY part of the last name. How do I make the condition only if it ENDS with these strings?
Issue: Will crash if last name contains ini or ez -- basically, only first If statement works.
Issue: Needs to be saved as Lastname, Firstname -- For now, it saves as LastnameFirstname.
Your program has multiple issues.
Some of the have been addressed by Ray and Stephen Lechner, but here are some more:
The reason for the crashes is you pass string to fwrite instead of FILE* stream pointers: fwrite(person.lastName, 1, strlen(person.lastName), ini); should be written:
fwrite(person.lastName, 1, strlen(person.lastName), ita);
This is an indication that you compile without proper warnings enabled. Let the compiler help avoid such silly mistakes with -Wall for gcc or /W3 for msvc.
Note also that you should use fprintf to properly format the output to your text files. For example:
if (strEndsWith(person.lastName, "ian")) {
fprintf(arm, "%s, %s\n", person.lastName, person.firstName);
}
Here is an improved version:
#include <stdio.h>
#include <string.h>
int strEndsWith(const char *str, const char *ending) {
size_t len1 = strlen(str);
size_t len2 = strlen(ending);
return len1 >= len2 && !strcmp(str + len1 - len2, ending);
}
int main(void) {
FILE *arm = fopen("C:\\Programming\\ARMENIA.TXT", "wt");
FILE *ita = fopen("C:\\Programming\\ITALIA.TXT", "wt");
FILE *esp = fopen("C:\\Programming\\ESPANIA.TXT", "wt");
if (arm == NULL || ita == NULL || esp == NULL) {
printf("Error: The archives could not be created.\n");
return 1;
} else {
printf("Operation Successful: Archives created.\n\n");
}
for (;;) {
char firstName[20];
char lastName[30];
char response;
printf("\n\nPlease input the first name: ");
if (scanf("%19s", firstName) != 1)
break;
printf("Please input the last name: ");
if (scanf("%29s", lastName) != 1)
break;
if (strEndsWith(lastName, "ian")) {
fprintf(arm, "%s, %s\n", lastName, firstName);
}
if (strEndsWith(lastName, "ini")) {
fprintf(ita, "%s, %s\n", lastName, firstName);
}
if (strEndsWith(lastName, "ez")) {
fprintf(esp, "%s, %s\n", lastName, firstName);
}
printf("\n\nWould you like to enter another person into the archive?: (y) or (n): ");
if (scanf(" %c", &response) != 1 || response != 'y')
break;
}
printf("\n\nThe operation has finished.\n\n");
fclose(arm);
fclose(ita);
fclose(esp);
return 0;
}
In addition to the issues Ray pointed out, see the following simple code for a strEndsWith-function. Hope it helps.
int strEndsWith(const char* str, const char* ending) {
size_t strlenStr = strlen(str);
size_t strlenEnding = strlen(ending);
if (strlenStr < strlenEnding)
return 0;
const char* strAtEnding = str + strlenStr - strlenEnding;
return (strcmp(strAtEnding, ending) == 0);
}
There are a number of unrelated issues here.
arm = fopen("C:\\Programming\\ARMENIA.TXT", "wt");
That should be "w+", not "wt". Same for the others. Or just "w", unless you want to both read and write.
if (arm, ita, esp == NULL)
The comma operator evaluates its left operand and discards it, then evaluates its right operand and keeps that value. So this is equivalent to if (esp == NULL)
fflush(stdin);
This is meaningless. When printing, the system is allowed to buffer output and then print it all at once, which can speed things up. fflush forces it to actually print anything that's currently in the buffer. It's useful if, for example, you want to prompt the user for input and need to make sure the prompt shows up.
As such, it only works on an output stream. (This error is actually taught in some terrible references, so I suspect this one isn't your fault. You should stop using whatever reference told you to do this.)
Addendum: As Olaf points out in the comments, this invokes undefined behavior, which is a topic unto itself. The short and imprecise version is that any undefined behavior creates bugs that are really hard to track down.
if (ret = strstr(person.lastName, ian))
This tells you that ian is in person.lastName somewhere, but not that it's at the end. For example, suppose the last name is "ian in ez". It'll match all three of your conditions. As an aside, you don't need to store ian in a variable; you can just do strstr(person.lastName, "ian").

how to read a text file in c and then split each line into tokens?

The input text file has some numbers per line, numbers are split by space. The first two lines only got one number, and the following lines got three. What I want to do is read each line of the input and store these numbers.
This is what I've got so far:
int
main(int argc, char *argv[]) {
int n = 0;
char buff[MAX_STRING_LEN]; //MAX_STRING_LEN is defined as 64
while (fgets(buff,MAX_STRING_LEN, stdin) != NULL) {
char temp;
if (n == 0) {
sscanf(buff, "%s", &temp);
int h_num = (int)temp;
} else if (n == 1) {
sscanf(buff, "%s", &temp);
int s_num = (int)temp;
} else {
sscanf(buff, "%s", &temp);
char *token;
token = strtok(&temp, " ");
int i = 0;
int a,b,c;
while (token != NULL) {
if (i == 0) {
a = (int)token;
token = strtok(NULL, " ");
} else if (i == 1) {
b = (int)token;
token = strtok(NULL, " ");
} else {
c = (int)token;
token = strtok(NULL, " ");
}
i++;
}
}
n++;
}
return 0;
}
The print statement I used to test my code is like:
printf("%d\n",h_num);
printf("%d\n%d\n%d\n",a,b,c);
I created a text file like this:
23
34
4 76 91
but the output is not what I expected, it's the address of the pointer I think. (I'm stuck with pointer again =( )
Could someone help me to point out what the problem is? Appreciate it.
In your code, I can see,
int h_num = (int)temp;
and
int s_num = (int)temp;
No, that is not how you convert an aphanumeric string to int.
You need to use strtol() for this purpose.
Then,
sscanf(buff, "%s", &temp);
is wrong. temp is a char, you got to use %c for that.
My suggestion for a better approach:
Read a complete line from file using fgets()
tokenize the input using strtok(), using space () as delimiter, then convert the token (if not NULL) to int using strtol()
continue untill the returned token is NULL
In this case, your code will be much more generic, as don't need to bother seperately about the number of ints present in each line.

fscanf to structure array

I'm trying to take some input from a text file, put it into a structure and print it out. The sample text file looks like this:
2
Curtis
660-------
Obama
2024561111
(Digits on the first number dashed out (for privacy), second is the Whitehouse.gov one, I called, they can't help me.)
Sample output:
204-456-1111 Obama
660--------- Curtis
(Formatting and sorting shouldn't be a problem when I figure out the rest.)
My question is labeled by the question marks below (in the first FOR loop, how do I get specific lines out of the text file to create the structures?
#include <stdio.h>
#include <string.h>
struct telephone {
char name[80];
long long int number;
}
main() {
struct telephone a, b;
char text[80];
int amount, i;
FILE *fp;
fp = fopen("phone.txt", "r");
fscanf(fp, "%d", amount);
struct telephone list[amount];
for(i = 0; i < amount; i++) {
strcpy(list[i].name, ???);
list[i].number, ???);
}
fclose(fp);
for(i = 0; i < amount; i++) {
DisplayStruct(list[i]);
}
}
DisplayStruct(struct telephone input) {
printf("%lld %s\n", input.number, input.name);
}
Use fgets to read one line at a time.
int lnum = 0;
char line[100];
while( fgets(line, sizeof(line), fp) ) {
lnum++;
printf( "Line %d : %s\n", lnum, line );
}
You can then use sscanf or strtok or numerous other approaches to pull data out of the string you just read.
I advise against storing your phone number as an integer. Phone numbers are better represented as strings.
If you can guarantee that neither names nor phone numbers have blanks in them, you can utilize fscanf() to read this data:
for(i = 0; i < amount; i++) {
fscanf("%s %lld", list[i].name, &list[i].phone);
}
Things to keep in mind:
You must check for conversion errors
This approach is less tolerant to input errors (in case of using fgets() it might be easier to recover and drop the malformed entry - unless the record has wrong number of fields).
Agree with #paddy, use a string to store phone numbers. (Cope with leading 0s, variant length, #, *, pause, etc.). Might as well also make sure it is big enough for a int64_t.
Note: The web has examples of 22 digits.
struct telephone {
char name[80];
char number[21];
}
To read in the data ...
for (i = 0; i < amount; i++) {
// +1 for buffer size as string read has a \n which is not stored.
char na[sizeof list[0].name + 1];
char nu[sizeof list[0].number + 1];
if ((fgets(na, sizeof na, fp) == NULL) || (fgets(nu, sizeof nu, fp) == NULL)) {
break; // TBD, Handle unexpected missing data
}
// The leading space in the format will skip leading white-spaces.
if (1 != sscanf(na, " %79[^\n]", list[i].name)) {
break; // TBD, Handle empty string
}
if (1 != sscanf(na, " %20[^\n]", list[i].number)) {
break; // TBD, Handle empty string
}
}
if (fgetc(fp) != EOF) {
; // Handle unexpected extra data
}
amount = i;
To write
// Pass address of structure
for(i = 0; i < amount; i++) {
DisplayStruct(&list[i]);
}
void DisplayStruct(const struct telephone *input) {
if (strlen(input->number) == 10) {
printf("%.3s-%.3s-%4s", input->number, &input->number[3], &input->number[6]);
}
else { // tbd format for unexpected telephone number length
printf("%13s", input->number);
}
// Suggest something around %s like \"%s\" to encapsulate names with spaces
printf(" %s\n", input->name);
}

Resources