C: Unable to store lines from a file into an array - c

This is some code I used to read and store lines from a text file into an "expressions" array:
//create array for going through the file
char lines[128];
//create array for storing expressions
char **expressions = malloc(128*sizeof(char*));
FILE *file = fopen(argv[1],"r");
int count = 0;
while (fgets(lines,128,file)){
expressions[count] = lines;
printf("expressions[%d] is %s\n",count,expressions[count]);
count++;
}
for (int i = 0; i<count; i++){
printf("%s",expressions[i]);
}
And this is the text this code is trying to read:
f = g + h - 42;
g = 12 + 23;
My issue here is that while it appears to go through the file properly (count matches the number of lines), the final print loop prints the last line g = 12 + 23 twice instead of the two distinct lines. Why is this occuring and what can I do to fix this issue?

Each time you read a line, you store it in the lines character array, and then you save the address of that array in the next space of expressions. If you look at the values stored in expressions you'll find that they are all the same.
If you want to keep a copy of each line, you're going to have to have space to store a copy of each line. If you have a maximum number of lines which you're going to deal with, you can allocate that memory in the program. Otherwise you're going to have to start using dynamic memory allocation.
Let's work on 100 lines maximum, with each line no longer than 127 characters (as above):
char expressions[100][128];
int count = 0;
while (fgets(lines,128,file)) {
strcpy(expressions[count], lines);
printf("expressions[%d] is %s\n",count,expressions[count]);
count++;
if (count == 100)
break;
}

Related

How to justify and print a paragraph using a 2d array in C?

I have an assignment that basically is asking to justify a paragraph given line length. So for instance the paragraph
"I am a student of C, this is my first assignment. I hope I finish on time." given line length of 17 should be as follows:
output
I am a student of
C, this is my
first assignment.
I hope I finish
on time.
I am having trouble with dynamically placing spacing in between the words. I currently have a function that counts the words in a paragraph and stores them into a 2d array but I have no idea how to a) calculate the amount of spacing in between words and b) how to dynamically print that justified paragraph.
Here is the code I have so far:
int getAllWordsFrom2DArray(char *paragraph, char words[MAX_NUMBER_OF_WORDS][MAX_WORD_LENGTH]) {
int i,j,totalWords = 0;
for(i=0; i < strlen(paragraph); i++) {
int wordLength;
if (paragraph[i] == ' ' || paragraph[i+1] == '\0') {
totalWords++;
wordLength = i;
for(j=0; j < wordLength; j++) {
words[i][j] = paragraph[j];
}
}
}
printf("%s", words);
return totalWords;
}
//Code in progress
int getNumberOfWordsForNextLine(int totalWords, int lineLength, char words[MAX_NUMBER_OF_WORDS][MAX_WORD_LENGTH]) {
int wordsForNextLine = 0;
for(int i=0; i < totalWords; i++) {
wordsForNextLine = 0 ;
}
}
//code in progress
void printNextLine(int wordsForNextLine) {
}
//skeleton code provided by instructor
void justifyAndPrintParagraph(char* paragraph, int lineLength) {
char words[MAX_NUMBER_OF_WORDS][MAX_WORD_LENGTH];
int totalWords = getAllWordsFrom2DArray(paragraph, words);
int processedWords = 0;
while (processedWords < totalWords) {
int wordsForNextLine = getNumberOfWordsForNextLine(totalWords, lineLength, words);
printNextLine(wordsForNextLine);
processedWords += wordsForNextLine;
}
}
To clarify, we are not allowed to use strlok. Essentially we are expected to just use the basics in doing this. I need to use the void justifyAndPrintParagraph function and signature but other than that I'm free to do whatever.
Edit: I forgot to add that if spaces cannot be evenly divided then the extra spaces are to be allocated left to right.
Any help is greatly appreciated.
Consider how many spaces you have to distribute. For example, given the input:
18
I am the very model of a modern Major-General.
Computing the number of words that fit on the line goes:
"I" + "am" + "the" + "very" + (4-1 words) --> 13
"I" + "am" + "the" + "very" + "model" + (5-1 words) --> 19
So only the first 4 words fit on an 18-character line. The number of space characters to distribute are then easily calculated:
N = max_line_width - sum_of_word_lengths
Now for the hard part: how many spaces between each word? Your homework expects you to divvy extra unbalanced spaces left-to-right, meaning that each pair of words may have a different number of space characters.
However, the difference will always be a single space character. Take a moment to convince yourself this is true:
I···am···the··very
-2-4-6-8-0-2-4-6-8
In our little example, we find that there are three space characters in the first two inter-word spacings, and two space characters in the last.
The minimum number of space characters per inter-word spacing is easy enough to caluclate:
nsp = N / (number_of_words_in_line - 1)
Beware! What happens if you have only one word on the line? (Do you really need to distribute spaces for such a line?)
And now, for the cool tricky math part, you can calculate the number of times you need to add a space to the inter-word spacing as:
nplus1 = N - nsp * (number_of_words_in_line - 1)
or just:
nplus1 = N % (number_of_words_in_line - 1)
Keep in mind that it is possible that all inter-word spacings are the same number of space characters, and may be exactly one space character even. Notice how our calculations work just as well in those cases.
Now you can print the words for the line in a loop, adding nsp space characters after every word, plus an extra space after the first nplus1 words.
Remember, the last word of the line doesn’t get any spaces. It is followed by a newline!
Hopefully this should help you work your way through this assignment.
(I personally think it is a bit of a careless assignment as your first ever, introduction to C class.)
And now, if I have made errors, it is because I am very, very sleepy. Someone will surely point it out if I have.
So using Dúthomhas' suggestion I was able to create the function below:
void justifyAndPrintLine(char words[MAX_NUMBER_OF_WORDS][MAX_WORD_LENGTH], int processedWords, int amountOfWordsForNextLine, int lineLength) {
int total = 0;
for (int i = processedWords; i < processedWords + amountOfWordsForNextLine; i++) {
total += (int) strlen(words[i]);
}
int spaces = lineLength - total;
int spacesBetweenWords = spaces / (amountOfWordsForNextLine - 1);
int spacesRemaining = spaces % (amountOfWordsForNextLine - 1);
int spaceForThisWord;
int leftWords = processedWords + amountOfWordsForNextLine;
while (processedWords != leftWords) {
spaceForThisWord = spacesBetweenWords;
if (spacesRemaining > 0) {
spaceForThisWord++;
spacesRemaining--;
}
printLine(words[processedWords], spaceForThisWord);
processedWords++;
}
}
A key part of my understanding of the math was that the difference in spacing was always going to a single space character. Borrowing his math I was able to properly justify the paragraph. Thanks again Dúthomhas!

Function to Split a String into Letters and Digits in C

I'm pretty new to C, and I'm trying to write a function that takes a user input RAM size in B, kB, mB, or gB, and determines the address length. My test program is as follows:
int bitLength(char input[6]) {
char nums[4];
char letters[2];
for(int i = 0; i < (strlen(input)-1); i++){
if(isdigit(input[i])){
memmove(&nums[i], &input[i], 1);
} else {
//memmove(&letters[i], &input[i], 1);
}
}
int numsInt = atoi(nums);
int numExponent = log10(numsInt)/log10(2);
printf("%s\n", nums);
printf("%s\n", letters);
printf("%d", numExponent);
return numExponent;
}
This works correctly as it is, but only because I have that one line commented out. When I try to alter the 'letters' character array with that line, it changes the 'nums' character array to '5m2'
My string input is '512mB'
I need the letters to be able to tell if the user input is in B, kB, mB, or gB.
I am confused as to why the commented out line alters the 'nums' array.
Thank you.
In your input 512mB, "mB" is not digit and is supposed to handled in commented code. When handling those characters, i is 3 and 4. But because length of letters is only 2, when you execute memmove(&letters[i], &input[i], 1);, letters[i] access out of bounds of array so it does undefined behaviour - in this case, writing to memory of nums array.
To fix it, you have to keep unique index for letters. Or better, for both nums and letters since i is index of input.
There are several problems in your code. #MarkSolus have already pointed out that you access letters out-of-bounds because you are using i as index and i can be more than 1 when you do the memmove.
In this answer I'll address some of the other poroblems.
string size and termination
Strings in C needs a zero-termination. Therefore arrays must be 1 larger than the string you expect to store in the array. So
char nums[4]; // Can only hold a 3 char string
char letters[2]; // Can only hold a 1 char string
Most likely you want to increase both arrays by 1.
Further, your code never adds the zero-termination. So your strings are invalid.
You need code like:
nums[some_index] = '\0'; // Add zero-termination
Alternatively you can start by initializing the whole array to zero. Like:
char nums[5] = {0};
char letters[3] = {0};
Missing bounds checks
Your loop is a for-loop using strlen as stop-condition. Now what would happen if I gave the input "123456789BBBBBBBB" ? Well, the loop would go on and i would increment to values ..., 5, 6, 7, ... Then you would index the arrays with a value bigger than the array size, i.e. out-of-bounds access (which is real bad).
You need to make sure you never access the array out-of-bounds.
No format check
Now what if I gave an input without any digits, e.g. "HelloWorld" ? In this case nothin would be written to nums so it will be uninitialized when used in atoi(nums). Again - real bad.
Further, there should be a check to make sure that the non-digit input is one of B, kB, mB, or gB.
Performance
This is not that important but... using memmove for copy of a single character is slow. Just assign directly.
memmove(&nums[i], &input[i], 1); ---> nums[i] = input[i];
How to fix
There are many, many different ways to fix the code. Below is a simple solution. It's not the best way but it's done like this to keep the code simple:
#define DIGIT_LEN 4
#define FORMAT_LEN 2
int bitLength(char *input)
{
char nums[DIGIT_LEN + 1] = {0}; // Max allowed number is 9999
char letters[FORMAT_LEN + 1] = {0}; // Allow at max two non-digit chars
if (input == NULL) exit(1); // error - illegal input
if (!isdigit(input[0])) exit(1); // error - input must start with a digit
// parse digits (at max 4 digits)
int i = 0;
while(i < DIGITS && isdigit(input[i]))
{
nums[i] = input[i];
++i;
}
// parse memory format, i.e. rest of strin must be of of B, kB, mB, gB
if ((strcmp(&input[i], "B") != 0) &&
(strcmp(&input[i], "kB") != 0) &&
(strcmp(&input[i], "mB") != 0) &&
(strcmp(&input[i], "gB") != 0))
{
// error - illegal input
exit(1);
}
strcpy(letters, &input[i]);
// Now nums and letter are ready for further processing
...
...
}
}

C - Incorrect result in counting number of empty elements in a 2D array

I am creating a program for a college assignment where the user is required to input artists and their songs. The program then sorts them alphabetically and shuffles them. Artist names are stored in an array called artists[][80] and song names are stored in songsArtistx, where x is a number from 1 to 4. I initialised all arrays to be filled with the NULL terminator - '\0'. For the program to work, I need to find the number of songs entered (have to be at least 1, but can be 3 or less). To achieve this, I am using a function called checkSongs:
int checkSongs(char songsOfAnArtist[][80])
{
int i,numOfSongs;
//Loop goes through 4 possible artists.
for (i=0;i<4;i++)
{
//Assume there are 3 songs for each artits, and decrement by 1 each time an empty string occurs.
numOfSongs = 3;
if (songsOfAnArtist[i][0]=='\0' || songsOfAnArtist [i][0] == '\n')
{
numOfSongs--;
break;
}
}
return numOfSongs;
}
However, this function gives me a faulty result for when the number of songs is less than 3. Here is an example from the command line, and also a screenshot of the variables from the debugger:
In the photo above, the numbers on the last line indicates the number of artists inputted (which is correct in this case) and the number of songs in songsArtsist1, songsArtsist2, songsArtsist3, songsArtsist4 respectively. The last number is the number of artists again.
How do I alter my code so that checkSongs returns the number of songs entered for each artists?
Below is also an excerpt from the main file which could be relevant to the question:
//Get all inputs from command line: artists and songs
getInputs(artists,songsArtist1,songsArtist2,songsArtist3,songsArtist4);
//Use checkArtists to store the number of entered artists in variable 'numOfArtists'
numOfArtists = checkArtists(artists);
printf("%d ",numOfArtists);
//Use check songs to store number of songs per artist in array 'numSongsPerArtists'
numSongsPerArtist[0] = checkSongs(songsArtist1);
numSongsPerArtist[1] = checkSongs(songsArtist2);
numSongsPerArtist[2] = checkSongs(songsArtist3);
numSongsPerArtist[3] = checkSongs(songsArtist4);
//DEBUG
printf("%d ",numSongsPerArtist[0]);
printf("%d ",numSongsPerArtist[1]);
printf("%d ",numSongsPerArtist[2]);
printf("%d ",numSongsPerArtist[3]);
printf("%d ",numOfArtists);
Here are there arrays:
//The array containing artists names
char artists[4][80];
//The array containing the sorted artists
char sortedArtists[4][80];
//Songs for Artist 1
char songsArtist1[3][80];
//Songs for Artist 2
char songsArtist2[3][80];
//Songs for Artist 3
char songsArtist3[3][80];
//Songs for Artist 4
char songsArtist4[3][80];
//The total number of artists (Note it can be less than 4)
int numOfArtists = 0;
//The total number of songs for each artist (Note that less than 3 songs can be provided for each artist)
int numSongsPerArtist[4] = {0,0,0,0};
When you write a function that takes an array as argument, it always should ask
for the length from the caller, unless the end of the array is marked somehow
(like '\0' for strings). If you later change you program to accepts more or
less number of songs and you forget to update the loop conditions, you are in a
world of trouble. The caller knows the size of the array, either because it
created the array or because the array along with it's size was passed. This is
standard behaviour of the functions in the standard C library.
So I'd rewrite your functions as:
int checkSongs(char songsOfAnArtist[][80], size_t len)
{
int numOfSongs = 0;
for(size_t i = 0; i < len; ++i)
{
if(songsOfAnArtist[i][0] != 0 && songsOfAnArtist[i][0] != '\n')
numOfSongs++;
}
return numOfSongs;
}
And then calling the function
numSongsPerArtist[0] = checkSongs(songsArtist1, sizeof songsArtist1 / sizeof *songsArtist1);
numSongsPerArtist[1] = checkSongs(songsArtist2, sizeof songsArtist2 / sizeof *songsArtist2);
numSongsPerArtist[2] = checkSongs(songsArtist3, sizeof songsArtist3 / sizeof *songsArtist3);
numSongsPerArtist[3] = checkSongs(songsArtist4, sizeof songsArtist4 / sizeof *songsArtist4);
This is better because if you later change from char songsArtist3[3][80]; to
char songsArtist3[5][80];, you don't have to rewrite the boundaries in the
loop conditions. Every artists can have a different size of slots for the songs.
And when you store the song names, make sure that the source string is not
longer than 79 characters long, use for example strncpy and make sure to write the '\0'-terminating byte.
If you keep having the wrong results, then it may be that the songsArtists
variables are not initialized correctly, please check your getInputs function
so that all songsArtist[i][0] are set to 0 on initialization. If you do
that, you should get the correct results.
For example:
int main(void)
{
...
char songsArtist1[3][80];
char songsArtist2[3][80];
char songsArtist3[3][80];
char songsArtist4[3][80];
memset(songsArtist1, 0, sizeof songsArtist1);
memset(songsArtist2, 0, sizeof songsArtist2);
memset(songsArtist3, 0, sizeof songsArtist3);
memset(songsArtist4, 0, sizeof songsArtist4);
...
}
Add a condition in the block where you break the loop.
if(sumOfSongs==0)
break;
Also I would recommend to use unsigned types for the variables as the numbers most likely can never be less than 0;
And put numOfSongs = 3; outside the for loop as others suggested.

Accounting for no existant characters as inputs C

Sorry if the question title is a little bit off, I had no idea what to call it just because it is such a peculiar question. What I am aiming to do is decode an input string encoded using a method I will explain in a bit, into a plain English text.
Encoding is done by choosing an integer nRows between 2 and half the length of the message, e.g. a message of length 11 would allow values of nRows in the range 2 to 5. The message is then written down the columns of a grid, one character in each grid cell, nRows in each column, until all message characters have been used. This may result in the last column being only partially filled. The message is then read out row-wise.
For example if the input message was ALL HAIL CAESAR, and the nRows value was 2, encoding would look like this:
A L H I A S R
L A L C E A #
Where # symbolizes a or blank character in the table, that doesn't actually exist - I have simply added it to explain the next part :)
The actual question I have is decoding these phrases. The code I have written thus far works for a few problems, but once the blank characters (#) become many the code begins to break down, as the code obviously does not register them and the algorithm skips past them.
My code is:
/*
* DeConfabulons.c
* A program to Decode for the Confabulons
*
* August 9th 2015
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
//A simple function confab which given input text encoded using
//the Confabulons encoding scheme, and a number of rows, returns
//the originally encoded phrase.
void deconfab(const char inText[], int nRows, char outText[])
{
int count = 0;
int i = 0;
int len = strlen(inText);
float help = ((float)len/(float)nRows);
int z = 0;
while (z < round(help))
{
while (((int)inText[count] > 0) && (count <= len))
{
outText[i] = inText[count];
i ++;
if (count < (int)help)
{
count = count + round((int)help+0.5);
}
else
{
float helper = count + help;
count = round(helper);
}
}
z ++;
count = z;
}
outText[i] = '\0';
}
Which thus far works for the Caesar example I gave earlier. The encoded form of it was ALHI ASRL ALCEA. The main(void) input I have been provided for that problem was:
char buffer[40] = {'\0'};
deconfab("ALHI ASRL ALCEA", 2, buffer);
printf("%s\n", buffer);
Which correctly outputs:
ALL HAIL CAESAR
However when working with cases with extra "blank" characters such as:
char buffer[60] = {0};
char* s = "Two hnvde eo frgqo .uxti hcjeku mlbparszo y";
deconfab(s, 13, buffer);
printf("%s\n", buffer);
The output should be:
The quick brown fox jumps over the lazy dog.
However my code will return:
Thdefq.the browneorouickmps ov g x julazy
I have concluded that this caused by the blank characters at the end in the last column by running through multiple tests by hand, however no matter what I try the code will not work for every test case. I am allowed to edit the bulk of the function in nearly any way, however any inputs or anything in int main(void) is not allowed to be edited.
I am simply looking for a way to have these blank characters recognized as characters without actually being there (as such) :)
First of all, as far as I see, you don't include those "null" characters in your input - if you did that (I guess) by adding any "dummy" characters, the algorithm would work. The reason it does in the first case is that the 'blank' character is missing at the end of the input - the same place as it's missing in the sentence.
You can try to make a workaround by guessing the length of a message with those dummy characters (I'm not sure how to formulate this) like:
ALHI ASRL ALCEA has 15 characters (15 mod 2 = 1) but ALHI ASRL ALCEA# has 16 characters. Similarly, Two hnvde eo frgqo .uxti hcjeku mlbparszo y has 44 characters (44 mod 13 = 5) so you need quite a lot of the dummy chars to make this work (13-5=8).
There are several ways at this point - you can for instance try to insert the missing blank spaces to align the columns, copy everything into a 2-dimensional array char by char, and then read it line by line, or just determine the (len mod rows) characters from the last column, remove them from the input (requires some fiddling with the classic C string functions so I won't give you the full answer here), read the rest and then append the characters from the last column.
I hope this helps.
There is some mess with index calculation.
At first it is pure discrete transformation. So, it should be implemented using only integer numbers.
The function below does what you need.
void deconfab(const char inText[], int nRows, char outText[])
{
int len = strlen(inText);
int cols = len / nRows;
int rows_with_large_cols = len % nRows;
int count = 0;
int col = 0;
int row = 0;
while (count < len)
{
int idx;
if (row < rows_with_large_cols)
idx = row * (cols + 1) + col;
else
idx = rows_with_large_cols * (cols + 1) +
(row - rows_with_large_cols) * cols + col;
if (idx > len - 1) {
++col;
row = 0;
idx = col;
}
outText[count] = inText[idx];
++row;
++count;
}
outText[count] = '\0';
}
It may be rewritten more nicely. Now it is like a pseudocode to explain the algorithm.
You cannot use the standard str* functions if you are going to handle nulls. You must, instead, work with the data directly and use the *read family of functions to get your data.

reading strings to a char array and then getting the size of the strings

Im working on a project and I am stumped on this part.
I need to read words from stdin and place them in a char array and use an array of pointers to point to each word since they will be jagged. where numwords is an int read in representing the number of words.
char words[10000];
char *wordp[2000];
the problem is that I can only use the pointers to add the words.I can no longer use the [] to help.
*wordp = words; //set the first pointer to the beginning of the char array.
while (t < numwords){
scanf("%s", *(wordp + t)) //this is the part I dont know
wordp = words + charcounter; //charcounter is the num of chars in the prev word
t++;
}
for(int i = 0;words+i != '\n';i++){
charcounter++;
}
any help would be great I am so confused when it comes to pointers and arrays.
Your code will be much more manageable if you use an additional pointer
reference and increment that directly. In this way you won't have to do any
mental math. Additionally you need to be incrementing the reference before
reading in the next string, scanf doesn't move the pointer for you.
char buffer[10000];
char* words[200];
int number_of_words = 200;
int current_words_index = 0;
// This is what we are going to use to write to the buffer
char* current_buffer_prt = buffer;
// quick memset (as I don't remember if c does this for us)
for (int i = 0; i < 10000; i++)
buffer[i] = '\0';
while (current_words_index < number_of_words) {
// Store a pointer to the current word before doing anything to it
words[current_word_index] = current_buffer_ptr;
// Read the word into the buffer
scanf("%s", current_buffer_ptr);
// NOTE: The above line could also be written
// scanf("%s", words[current_word_index]);
// this is how we move the buffer to it's next empty position.
while (current_buffer_ptr != '\n')
current_buffer_ptr++;
// this ensures we don't overwrite the previous \n char
current_buffer_ptr++;
current_words_index += 1;
}
What you want to do is relatively straightforward. You've got an array of 10,000 chars for storage, and 2000 pointers. So to start with you'll want to assign the first pointer to the start of the array:
wordp[0] = &words[0];
In pointer form this is:
*(wordp + 0) = words + 0;
I've used the zeros to show how it relates to the arrays. In general, to set each pointer to each element:
*(wordp + i) == wordp[i]
words + i == &words[i]
So all you need to do is keep track of where you are in the pointer array, and as long as you've assigned correctly, the pointer array will keep track of the position in your char array.

Resources