I have an assignment where we take in the contents from an input file and put it into arrys
the numbers on the first line represent the dimensions for the array. the first 6 regards the first 6 rows/lines on the text file, which i have put in a one dimensional ray but the characters after that i have to put them in a 5x6 2d array in the form of a solved crossword (with the spaces included and making sure to have a n extra column for '\0'so 5x7) I have to use malloc or calloc to make the array and my issue is making the matrix as it doesnt get the spaces or i get a segmentation dump. Im trying to make the arrays first(two arrays - one dimensional array of W strings and a 2d array of characters NxM) and make sure the elements are in the matrix but like i said i keep getting a segmentation dump. Im at a loss as to how to do this.
txt file:
6 5 6
nail
taco
name
men
next
can
next
namect
e aa
n nc
nail
here's my code so far:
void freeMatrix(char **matrix, int r, int c);
int main(int argc, char **argv)
{
int W, N, M;
char inBuf[SIZE], words[SIZE], crosswords[SIZE];
char *word;
char *outp;
FILE *input;
// FILE *output;
if (argc != 2)
{
printf("Error, please input the filenames\n");
exit(-1);
}
if ((input = fopen(argv[1], "r")) == NULL)
{
printf("Error, the file %s cannot be opened\n", argv[1]);
}
fgets(inBuf, SIZE, input);
sscanf(inBuf, "%i%i%i", &W, &N, &M);
word = (char *)malloc(sizeof(char) * W);
char *crossword[N];
char *ptr = word;
char token[N][M];
for (int x = 0; x < W; x++)
{
fgets(inBuf, SIZE, input);
sscanf(inBuf, "%s", &words);
printf("%s\n", words);
*ptr = *words;
}
for (int f = 0; f < N; f++)
{
crossword[f] = (char *)malloc(N * sizeof(char));
}
for (int y = 0; y < N; y++)
{
for (int z = 0; z < M; z++)
{
fscanf(input, "%c", &token[y][z]);
crossword[y][z] = token[y][z];
printf("%s", crossword[y][z]);
}
printf("\n");
}
fclose(input);
free(word);
freeMatrix(crossword, N, M);
return 0;
}
void freeMatrix(char **matric, int r, int c)
{
for (int i = 0; i < r; i++)
{
free(matric[i]);
}
free(matric);
}
I expected a crossword to be printed with the individual characters in the matrix the same way the text file has it set up. A segmentation error resulted. It prints the first 6 words fine as an array of strings but the 2d array of characters is where i fall short.
nail
taco
name
men
next
can
Segmentation fault (core dumped)
A lot of things wrong here. My first suggestion is just to get the easy bit working first. Read in the 6 words into an array and then print them out. Get that working first. (that isnt working in your code at the moment, you do not save the words then get entered anywhere). Here is whats wrong at the moment
fgets(inBuf, SIZE, input); // ok
sscanf(inBuf, "%i%i%i", &W, &N, &M); //ok
what is the purpose of word below? It is an array of 6 characters. There seems no point to it since 6 is the number of words, not the length of them
word = (char *)malloc(sizeof(char) * W);
char *crossword[N];
char *ptr = word;
// char token[N][M]; <<<< dont need this yet
// ok this loop read W words from the input file.
for (int x = 0; x < W; x++)
{
fgets(inBuf, SIZE, input); // good
sscanf(inBuf, "%s", &words); // you could have fscanf directly but ok
printf("%s\n", words); // print it
*ptr = *words; // meaningless line
}
I think that last line is somehow trying to copy the word you just read into the buffer 'word' given that you did *ptr = word earlier
First note the odd names, I would expect 'words' to be the array or wards and 'word' to be the one you just read.
More importantly thats not how you copy strings
So lets fix this
First we need an array of pointers to words
char *words[N];
I am reluctant to type that because its not standard c but you use it every where (Variable length arrays, my compiler does not allow it at all, even if I ask really nicely). But I will do it.
now we need a char buffer to read the current word into
char word[SIZE]; // get rid of old word and words
now lets do some reading
// get rid of old word and words
char word[SIZE];
char *words[N];
for (int x = 0; x < W; x++)
{
fgets(inBuf, SIZE, input);
sscanf(inBuf, "%s", &word);
words[x] = strdup(word);
}
now
for (int x = 0; x < W; x++)
{
printf("%s\n", words[x];
}
to prove we actually have the data
get all that workinig before moving on to the next section
PS, strdup is a wonderful function that mallocs the correct size for the string and then copies the string to the malloced buffer, returnig a pointer to the newly mallocd memory
There are several common mistakes present in this program.
If fopen fails, you print a warning but continue with the program anyway. You should exit here, or otherwise handle the event to prevent future reads from failing.
It would also be prudent to print errors to the standard error stream.
if ((input = fopen(argv[1], "r")) == NULL)
{
/* printf("Error, the file %s cannot be opened\n", argv[1]); */
fprintf(sterr, "Failed to open [%s].\n", argv[1]);
return EXIT_FAILURE;
}
Casting the result of malloc (or calloc, etc.) is not necessary in C. A void * can be safely and implicitly promoted to any other pointer type.
In addition to this, sizeof (char) is guaranteed to be 1.
/* word = (char *)malloc(sizeof(char) * W); */
word = malloc(W);
In sscanf(inBuf, "%s", &words), &words is a char (*)[SIZE], which is the wrong type for %s.
The & is not needed. words is of type char [SIZE], which will decay to the appropriate char * type when passed to a function.
fgets, fscanf, and sscanf can all fail. You should guard against this to prevent the rest of your program working with incorrect data.
fgets returns NULL on failure. *scanf functions return the number of successful conversions performed ("%s" is one conversion, "%i%i%i" is three).
if (NULL == fgets(inBuf, SIZE, input))
/* handle this event */;
if (1 != sscanf(inBuf, "%s", words))
/* handle this event */;
*ptr = *words; does not perform a string copy. Actually, it copies the first element of words to the first element of ptr.
ptr = words would assign the pointer value.
= is never used for string copying, and instead, copying is done with the strcpy function:
char one[64];
char two[] = "hello world."
strcpy(one, two);
Care must be taken to ensure that the destination buffer has enough space for the to-be-copied string (including its null terminating byte).
crossword[y][z] is a char. Use %c not %s to print single characters.
printf(/* "%s" */ "%c", crossword[y][z]);
The scanf specifier %s skips leading whitespace, begins reading characters into its buffer, and stops when it encounters more whitespace.
The result of sscanf(" e aa", "%s", buffer) would have buffer be equivalent to "e". You will not be able to parse the lines with spaces in them this way.
The character set conversion specifier (%[]) can be used to overcome this limitation, as it allows reading whitespace. %[^\n] will read all characters until it encounters a newline.
Note that using either %s or %[] without a limiting field width specifier is unsafe as gets, as you can overflow the buffer.
char buffer[16];
if (1 == sscanf(" e aa\n", "%15[^\n]", buffer))
printf("[%s]", buffer); /* => [ e aa] */
With that said, the simple solution is to just remove the newline from the input buffer (replace it with the null terminating byte), if it exists, and use that buffer directly.
char input[] = " e aa\n";
input[strcspn(input, "\n")] = 0;
The elements of crossword are initialized as
crossword[f] = (char *)malloc(N * sizeof(char));
with N as the length of the second dimension. They are then accessed as
for (int z = 0; z < M; z++) {
/* ... */
crossword[y][z] = token[y][z];
where z relies on a different bound (M instead of N). If N is 5, and M is 6, this will access memory out of bounds.
In general, there is no need for all these different, intermediate buffers (and pointers to buffers). This can be done with a single buffer for reading each line.
Here is a working example with some helper functions to reduce code repetition. The bulk of the program is in the run function. It should be relatively straightforward to follow along:
read the header information
allocate enough space for our word_count pointers to strings.
read word_count lines, for each:
allocate enough memory for it
copy the string to this allocated memory
allocate enough memory for a rows * cols matrix
read rows lines, for each:
copy each byte from the string (excluding the null byte) to the correct row and column in the matrix
use the data (display it)
free all allocations
Note that the input file must contain trailing whitespace on lines that require padding up to the specified cols length (truncation occurs if oversized).
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* output formatted message to stderr and exit program unsuccessfully */
static void panic(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
fputc('\n', stderr);
va_end(args);
exit(EXIT_FAILURE);
}
/* read a line of input into a size limited buffer; removes newline character */
static size_t read_file_line(char *buffer, int size, FILE *file) {
if (!fgets(buffer, size, file))
panic("Could not read user input.");
size_t length = strcspn(buffer, "\n");
buffer[length] = 0;
return length;
}
/* panic unless memory allocation successful */
static void *palloc(size_t bytes) {
void *p = malloc(bytes);
if (!p)
panic("Could not allocate %zu bytes.", bytes);
return p;
}
static void print_matrix(size_t r, size_t c, char (*m)[r][c]) {
putchar('+');
for (size_t i = 0; i < c; i++)
putchar('-');
puts("+");
for (size_t i = 0; i < r; i++) {
putchar('|');
for (size_t j = 0; j < c; j++)
putchar((*m)[i][j]);
puts("|");
}
putchar('+');
for (size_t i = 0; i < c; i++)
putchar('-');
puts("+");
}
static void run(FILE *file) {
size_t word_count;
size_t rows;
size_t cols;
char input[128];
/* read our header information */
read_file_line(input, sizeof input, file);
if (3 != sscanf(input, "%zu%zu%zu", &word_count, &rows, &cols))
panic("Could not read file header.");
/* allocate the word list, read and allocate each word */
char **words = palloc(sizeof *words * word_count);
for (size_t i = 0; i < word_count; i++) {
size_t len = read_file_line(input, sizeof input, file);
words[i] = palloc(len + 1);
strcpy(words[i], input);
}
/* allocate and read our matrix */
char (*matrix)[rows][cols] = palloc(sizeof *matrix);
for (size_t i = 0; i < rows; i++) {
size_t len = read_file_line(input, sizeof input, file);
if (len < cols)
panic("Insufficient column data: Required %zu, Read %zu", cols, len);
for (size_t j = 0; j < cols; j++)
(*matrix)[i][j] = input[j];
}
/* display our input */
for (size_t i = 0; i < word_count; i++)
printf("WORD: %s\n", words[i]);
print_matrix(rows, cols, matrix);
/* free the memory */
for (size_t i = 0; i < word_count; i++)
free(words[i]);
free(words);
free(matrix);
}
int main(int argc, char **argv) {
if (argc != 2)
panic("usage: %s FILENAME", argv[0]);
FILE *file = fopen(argv[1], "r");
if (!file)
panic("Could not open file [%s].", argv[1]);
run(file);
fclose(file);
}
stdout:
WORD: nail
WORD: taco
WORD: name
WORD: men
WORD: next
WORD: can
+------+
| next |
|namect|
| e aa|
| n nc|
| nail |
+------+
Related
i'm trying to implement little program that takes a text and breaks it into lines and sort them in alphabetical order but i encountered a little problem, so i have readlines function which updates an array of pointers called lines, the problem is when i try to printf the first pointer in lines as an array using %s nothing is printed and there is no errors.
I have used strcpy to copy an every single text line(local char array) into a pointer variable and then store that pointer in lines array but it gave me the error.
Here is the code:
#include <stdio.h>
#define MAXLINES 4
#define MAXLENGTH 1000
char *lines[MAXLINES];
void readlines() {
int i;
for (i = 0; i < MAXLINES; i++) {
char c, line[MAXLENGTH];
int j;
for (j = 0; (c = getchar()) != '\0' && c != '\n' && j < MAXLENGTH; j++) {
line[j] = c;
}
lines[i] = line;
}
}
int main(void) {
readlines();
printf("%s", lines[0]);
getchar();
return 0;
}
One problem is the following line:
lines[i] = line;
In this line, you make lines[i] point to line. However, line is a local char array whose lifetime ends as soon as the current loop iteration ends. Therefore, lines[i] will contain a dangling pointer (i.e. a pointer to an object that is no longer valid) as soon as the loop iteration ends.
For this reason, when you later call
printf("%s", lines[0]);
lines[0] is pointing to an object whose lifetime has ended. Dereferencing such a pointer invokes undefined behavior. Therefore, you cannot rely on getting any meaningful output, and your program may crash.
One way to fix this would be to not make lines an array of pointers, but rather an multidimensional array of char, i.e. an array of strings:
char lines[MAXLINES][MAXLENGTH+1];
Now you have a proper place for storing the strings, and you no longer need the local array line in the function readlines.
Another issue is that the line
printf("%s", lines[0]);
requires that lines[0] points to a string, i.e. to an array of characters terminated by a null character. However, you did not put a null character at the end of the string.
After fixing all of the issues mentioned above, your code should look like this:
#include <stdio.h>
#define MAXLINES 4
#define MAXLENGTH 1000
char lines[MAXLINES][MAXLENGTH+1];
void readlines() {
int i;
for (i = 0; i < MAXLINES; i++) {
char c;
int j;
for (j = 0; (c = getchar()) != '\0' && c != '\n' && j < MAXLENGTH; j++) {
lines[i][j] = c;
}
//add terminating null character
lines[i][j] = '\0';
}
}
int main(void) {
readlines();
printf("%s", lines[0]);
return 0;
}
However, this code still has a few issues, which are probably unrelated to your immediate problem, but could cause trouble later:
The function getchar will return EOF, not '\0', when there is no more data (or when an error occurred). Therefore, you should compare the return value of getchar with EOF instead of '\0'. However, a char is not guaranteed to be able to store the value of EOF. Therefore, you should store the return value of getchar in an int instead. Note that getchar returns a value of type int, not char.
When j reaches MAX_LENGTH, you will call getchar one additional time before terminating the loop. This can cause undesired behavior, such as your program waiting for more user input or an important character being discarded from the input stream.
In order to also fix these issues, I recommend the following code:
#include <stdio.h>
#define MAXLINES 4
#define MAXLENGTH 1000
char lines[MAXLINES][MAXLENGTH+1];
void readlines() {
int i;
for (i = 0; i < MAXLINES; i++)
{
//changed type from "char" to "int"
int c;
int j;
for ( j = 0; j < MAXLENGTH; j++ )
{
if ( (c = getchar()) == EOF || c == '\n' )
break;
lines[i][j] = c;
}
//add terminating null character
lines[i][j] = '\0';
}
}
int main(void) {
readlines();
printf("%s", lines[0]);
return 0;
}
Problem 1
char *lines[MAXLINES];
For the compiler it makes no difference how you write this, but for you, as you are learning C, maybe it is worth consider different spacing and naming. Question is: what is lines[]? lines[] is supposed to be an array of strings and hold some text inside. So lines[0] is a string, lines[1] is a string and so on. As pointed in a comment you could also use char lines[MAX_LINES][MAX_LENGTH] and have a 2D box of NxM char. This way you would have a pre-determined size in terms of number and size of lines and have simpler things at a cost of wasting space in lines of less than MAX_LENGTH chars and having a fixed number of lines you can use, but no need to allocate memory.
A more flexible way is to use an array of pointers. Since each pointer will represent a line, a single one
char* line[MAXLINES];
is a better picture of the use: line[0] is char*, line[1] is char* and so on. But you will need to allocate memory for each line (and you did not) in your code.
Remember int main(int argc, char**argv)
This is the most flexible way, since in this way you can hold any number of lines. The cost? Additional allocations.
size_t n_lines;
char** line;
This may be the best representation, as known by every C program since K&R.
Problem 2
for (
j = 0;
(c = getchar()) != '\0' && c != '\n' && j < MAXLENGTH;
j++) {
line[j] = c;
}
lines[i] = line;
This loop does not copy the final 0 that terminates each string. And reuses the same line, a char[] to hold the data as being read. And the final line does not copy a string, if one existed there. There is no one since the final 0 was stripped off by the loop. And there is no data too, since the area is being reused.
A complete C example of uploading a file to a container in memory
I will let an example of a more controlled way of writing this, a container for a set of lines and even a sorting function.
a data structure
The plan is to build an array of pointers as the system does for main. Since we do no know ahead the number of lines and do not want this limitation we will allocate memory in groups of blk_size lines. At any time we have limit pointers to use. From these size are in use. line[] is char* and points to a single line of text. The struct is
typedef struct
{
size_t blk_size; // block
size_t limit; // actual allocated size
size_t size; // size in use
char** line; // the lines
} Block;
the test function
Block* load_file(const char*);
Plan is to call load_file("x.txt") and the function returns a Block* pointing to the array representing the lines in file, one by one. Then we call qsort() and sort the whole thing. If the program is called lines we will run
lines x.txt
and it will load the file x.txt, show its contents on screen, sort it, show the sorted lines and then erase everything at exit.
main() for the test
int main(int argc, char** argv)
{
char msg[80] = {0};
if (argc < 2) usage();
Block* test = load_file(argv[1]);
sprintf(msg, "==> Loading \"%s\" into memory", argv[1]);
status_blk(test, msg);
qsort(test->line, test->size, sizeof(void*), cmp_line);
sprintf(msg, "==> \"%s\" after sort", argv[1]);
status_blk(test, msg);
test = delete_blk(test);
return 0;
};
As planned
load_file() is the constructor and load the file contents into a Block.
status_blk() shows the contents and accepts a convenient optional message
qsort() sorts the lines using a one-line cmp_line() function.
status_blk() is called again and shows the now sorted contents
as in C++ delete_blk() is the destructor and erases the whole thing._
output using main() as tlines.c for testing
PS M:\> .\lines tlines.c
loading "tlines.c" into memory
Block extended for a total of 16 pointers
==> Loading "tlines.c" into memory
Status: 13 of 16 lines. [block size is 8]:
1 int main(int argc, char** argv)
2 {
3 char msg[80] = {0};
4 if (argc < 2) usage();
5 Block* test = load_file(argv[1]);
6 sprintf(msg, "==> Loading \"%s\" into memory", argv[1]);
7 status_blk(test, msg);
8 qsort(test->line, test->size, sizeof(void*), cmp_line);
9 sprintf(msg, "==> \"%s\" after sort", argv[1]);
10 status_blk(test, msg);
11 test = delete_blk(test);
12 return 0;
13 };
==> "tlines.c" after sort
Status: 13 of 16 lines. [block size is 8]:
1 Block* test = load_file(argv[1]);
2 char msg[80] = {0};
3 if (argc < 2) usage();
4 qsort(test->line, test->size, sizeof(void*), cmp_line);
5 return 0;
6 sprintf(msg, "==> Loading \"%s\" into memory", argv[1]);
7 sprintf(msg, "==> \"%s\" after sort", argv[1]);
8 status_blk(test, msg);
9 status_blk(test, msg);
10 test = delete_blk(test);
11 int main(int argc, char** argv)
12 {
13 };
About the code
I am not sure if it needs much explanation, it is a single function that does the file loading and it has around 20 lines of code. The other functions has less than 10. The whole file is represented in line that is char** and Block has the needed info about actual size.
Since line[] is an array of pointers we can call
qsort(test->line, test->size, sizeof(void*), cmp_line);
and use
int cmp_line(const void* one, const void* other)
{
return strcmp(
*((const char**)one), *((const char**)other));
}
using strcmp() to compare the strings and have the lines sorted.
create_blk() accepts a block size for use in the calls to realloc() for eficiency.
Delete a Block is a 3-step free() in the reverse order of allocation.
The complete code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
size_t blk_size; // block
size_t limit; // actual allocated size
size_t size; // size in use
char** line; // the lines
} Block;
Block* create_blk(size_t);
Block* delete_blk(Block*);
int status_blk(Block*, const char*);
Block* load_file(const char*);
int cmp_line(const void*, const void*);
void usage();
int main(int argc, char** argv)
{
char msg[80] = {0};
if (argc < 2) usage();
Block* test = load_file(argv[1]);
sprintf(msg, "\n\n==> Loading \"%s\" into memory", argv[1]);
status_blk(test, msg);
qsort(test->line, test->size, sizeof(void*), cmp_line);
sprintf(msg, "\n\n==> \"%s\" after sort", argv[1]);
status_blk(test, msg);
test = delete_blk(test);
return 0;
};
int cmp_line(const void* one, const void* other)
{
return strcmp(
*((const char**)one), *((const char**)other));
}
Block* create_blk(size_t size)
{
Block* nb = (Block*)malloc(sizeof(Block));
if (nb == NULL) return NULL;
nb->blk_size = size;
nb->limit = size;
nb->size = 0;
nb->line = (char**)malloc(sizeof(char*) * size);
return nb;
}
Block* delete_blk(Block* blk)
{
if (blk == NULL) return NULL;
for (size_t i = 0; i < blk->size; i += 1)
free(blk->line[i]); // free lines
free(blk->line); // free block
free(blk); // free struct
return NULL;
}
int status_blk(Block* bl,const char* msg)
{
if (msg != NULL) printf("%s\n", msg);
if (bl == NULL)
{
printf("Status: not allocated\n");
return -1;
}
printf(
"Status: %zd of %zd lines. [block size is %zd]:\n",
bl->size, bl->limit, bl->blk_size);
for (int i = 0; i < bl->size; i += 1)
printf("%4d\t%s", 1 + i, bl->line[i]);
return 0;
}
Block* load_file(const char* f_name)
{
if (f_name == NULL) return NULL;
fprintf(stderr, "loading \"%s\" into memory\n", f_name);
FILE* F = fopen(f_name, "r");
if (F == NULL) return NULL;
// file is open
Block* nb = create_blk(8); // block size is 8
char line[200];
char* p = &line[0];
p = fgets(p, sizeof(line), F);
while (p != NULL)
{
// is block full?
if (nb->size >= nb->limit)
{
const size_t new_sz = nb->limit + nb->blk_size;
char* new_block =
realloc(nb->line, (new_sz * sizeof(char*)));
if (new_block == NULL)
{
fprintf(
stderr,
"\tCould not extend block to %zd "
"lines\n",
new_sz);
break;
}
printf(
"Block extended for a total of %zd "
"pointers\n",
new_sz);
nb->limit = new_sz;
nb->line = (char**)new_block;
}
// now copy the line
nb->line[nb->size] = (char*)malloc(1 + strlen(p));
strcpy(nb->line[nb->size], p);
nb->size += 1;
// read next line
p = fgets(p, sizeof(line), F);
}; // while()
fclose(F);
return nb;
}
void usage()
{
fprintf(stderr,"Use: program file_to_load\n");
exit(EXIT_FAILURE);
}
Try something like this:
#include <stdio.h>
#include <stdlib.h> // for malloc(), free(), exit()
#include <string.h> // for strcpy()
#define MAXLINES 4
#define MAXLENGTH 1000
char *lines[MAXLINES];
void readlines() {
for( int i = 0; i < MAXLINES; i++) {
char c, line[MAXLENGTH + 1]; // ALWAYS one extra to allow for '\0'
int j = 0;
// RE-USE(!) local array for input characters until NL or length
// NB: Casting return value to character (suppress warning)
while( (c = (char)getchar()) != '\0' && c != '\n' && j < MAXLENGTH )
line[ j++ ] = c;
line[j] = '\0'; // terminate array (transforming it to 'string')
// Attempt to get a buffer to preserve this line
// (Old) compiler insists on casting return from malloc()
if( ( lines[i] = (char*)malloc( (j + 1) * sizeof lines[0][0] ) ) == NULL ) {
fprintf( stderr, "malloc failure\n" );
exit( -1 );
}
strcpy( lines[i], line ); // preserve this line
}
}
int my_main() {
readlines(); // only returns after successfully reading 4 lines of input
for( int i = 0; i < MAXLINES; i++)
printf( "Line %d: '%s'\n", i, lines[i] ); // enhanced
/* Maybe do stuff here */
for( int j = 0; j < MAXLINES; j++) // free up allocated memory.
free( lines[j] );
return 0;
}
If you would prefer to 'factor out` some code (and have a facility that you've written is absent, here's a version:
char *my_strdup( char *str ) {
int len = strlen( str ) + 1; // ALWAYS +1
// Attempt to get a buffer to preserve this line
// (Old) compiler insists on casting return from malloc()
char *pRet = (char*)malloc( len * sizeof *pRet );
if( pRet == NULL ) {
fprintf( stderr, "malloc failure\n" );
exit( -1 );
}
return strcpy( pRet, str );
}
The the terminating and preserve is condensed to:
line[j] = '\0'; // terminate array (transforming it to 'string')
lines[i] = my_strdup( line ); // preserve this line
I want to find how many names in names array. I know sizeof(names)/sizeof(names[0]) gives the right answer. But the problem is I can't just declare char *names[];. Because compiler gives me an error like this "Storage of names is unknown". To avoid this error, I must declare like this char *names[] = {"somename", "somename2"};. But the thing is I cannot assign the strings right after deceleration. I assign strings after some conditions and my problem is how many strings i have after that conditions.
My example.
char *names[];
char word[10];
int i = 0;
while (fscanf(word, sizeof(word), fp)>0) {
// Think hello increase every time loop returns.
// such as "hello1", and the 2nd time "hello2"
if(strcmp(word, "hello1") == 0)
names[i] = word;
}
printf("size: %d\n", sizeof(names)/sizeof(names[0]));
An array size is fixed once the array is created. It cannot change.
If fp can be read twice, read the file once for the word count.
size_t word_count = 0;
int word_length_max = 0;
long pos = ftell(fp); // remember file location
int n = 0;
while (fscanf(fp, "%*s%n", &n) != EOF && n > 0) { // Use %n to record character count
word_count++;
if (n > word_length_max) {
word_length_max = n;
}
n = 0;
}
Now code knows the word[] array size needed and the maximum length.
char *words[word_count];
char word[word_length_max + 1u]; // buffer size needed to read in the words
fseek(fp, pos, SEEK_SET); // go back
for (size_t i=0; i<word_count; i++) {
if (fscanf(fp, "%s", word) != 1) {
Handle_UnexpectedError(); // 2nd pass should have same acceptable results
}
words[i] = strdup(word); // allocate a duplicate
}
When done with words[], be sure to free the allocated memory.
....
for (size_t i=0; i<word_count; i++) {
free(words[i]);
}
Better code would also check the return value of ftell(), fseek(), malloc() for errors and limit fscanf(fp, "%s", word).
I wanted to know if there was a way to use scanf so I can take in an unknown number of string arguments and put them into a char* array. I have seen it being done with int values, but can't find a way for it to be done with char arrays. Also the arguments are entered on the same line separated by spaces.
Example:
user enters hello goodbye yes, hello gets stored in array[0], goodbye in array[1] and yes in array[2]. Or the user could just enter hello and then the only thing in the array would be hello.
I do not really have any code to post, as I have no real idea how to do this.
You can do something like, read until the "\n" :
scanf("%[^\n]",buffer);
you need to allocate before hand a big enough buffer.
Now go through the buffer count the number of words, and allocate the necessary space char **array = ....(dynamic string allocation), go to the buffer and copy string by string into the array.
An example:
int words = 1;
char buffer[128];
int result = scanf("%127[^\n]",buffer);
if(result > 0)
{
char **array;
for(int i = 0; buffer[i]!='\0'; i++)
{
if(buffer[i]==' ' || buffer[i]=='\n' || buffer[i]=='\t')
{
words++;
}
}
array = malloc(words * sizeof(char*));
// Using RoadRunner suggestion
array[0] = strtok (buffer," ");
for(int w = 1; w < words; w++)
{
array[w] = strtok (NULL," ");
}
}
As mention in the comments you should use (if you can) fgets instead fgets(buffer,128,stdin);.
More about strtok
If you have an upper bound to the number of strings you may receive from the user, and to the number of characters in each string, and all strings are entered on a single line, you can do this with the following steps:
read the full line with fgets(),
parse the line with sscanf() with a format string with the maximum number of %s conversion specifiers.
Here is an example for up to 10 strings, each up to 32 characters:
char buf[400];
char s[10][32 + 1];
int n = 0;
if (fgets(buf, sizeof buf, sdtin)) {
n = sscanf("%32s%32s%32s%32s%32s%32s%32s%32s%32s%32s",
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9]));
}
// `n` contains the number of strings
// s[0], s[1]... contain the strings
If the maximum number is not known of if the maximum length of a single string is not fixed, or if the strings can be input on successive lines, you will need to iterate with a simple loop:
char buf[200];
char **s = NULL;
int n;
while (scanf("%199s", buf) == 1) {
char **s1 = realloc(s, (n + 1) * sizeof(*s));
if (s1 == NULL || (s1[n] = strdup(buf)) == NULL) {
printf("allocation error");
exit(1);
}
s = s1;
n++;
}
// `n` contains the number of strings
// s[0], s[1]... contain pointers to the strings
Aside from the error handling, this loop is comparable to the hard-coded example above but it still has a maximum length for each string. Unless you can use a scanf() extension to allocate the strings automatically (%as on GNU systems), the code will be more complicated to handle any number of strings with any possible length.
You can use:
fgets to read input from user. You have an easier time using this instead of scanf.
malloc to allocate memory for pointers on the heap. You can use a starting size, like in this example:
size_t currsize = 10
char **strings = malloc(currsize * sizeof(*strings)); /* always check
return value */
and when space is exceeded, then realloc more space as needed:
currsize *= 2;
strings = realloc(strings, currsize * sizeof(*strings)); /* always check
return value */
When finished using the requested memory from malloc() and realloc(), it's always to good to free the pointers at the end.
strtok to parse the input at every space. When copying over the char * pointer from strtok(), you must also allocate space for strings[i], using malloc() or strdup.
Here is an example I wrote a while ago which does something very similar to what you want:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INITSIZE 10
#define BUFFSIZE 100
int
main(void) {
char **strings;
size_t currsize = INITSIZE, str_count = 0, slen;
char buffer[BUFFSIZE];
char *word;
const char *delim = " ";
int i;
/* Allocate initial space for array */
strings = malloc(currsize * sizeof(*strings));
if(!strings) {
printf("Issue allocating memory for array of strings.\n");
exit(EXIT_FAILURE);
}
printf("Enter some words(Press enter again to end): ");
while (fgets(buffer, BUFFSIZE, stdin) != NULL && strlen(buffer) > 1) {
/* grow array as needed */
if (currsize == str_count) {
currsize *= 2;
strings = realloc(strings, currsize * sizeof(*strings));
if(!strings) {
printf("Issue reallocating memory for array of strings.\n");
exit(EXIT_FAILURE);
}
}
/* Remove newline from fgets(), and check for buffer overflow */
slen = strlen(buffer);
if (slen > 0) {
if (buffer[slen-1] == '\n') {
buffer[slen-1] = '\0';
} else {
printf("Exceeded buffer length of %d.\n", BUFFSIZE);
exit(EXIT_FAILURE);
}
}
/* Parsing of words from stdin */
word = strtok(buffer, delim);
while (word != NULL) {
/* allocate space for one word, including nullbyte */
strings[str_count] = malloc(strlen(word)+1);
if (!strings[str_count]) {
printf("Issue allocating space for word.\n");
exit(EXIT_FAILURE);
}
/* copy strings into array */
strcpy(strings[str_count], word);
str_count++;
word = strtok(NULL, delim);
}
}
/* print and free strings */
printf("Your array of strings:\n");
for (i = 0; i < str_count; i++) {
printf("strings[%d] = %s\n", i, strings[i]);
free(strings[i]);
strings[i] = NULL;
}
free(strings);
strings = NULL;
return 0;
}
It would be better If I show you guys an example of what my program is supposed to do.
Input:
3
Double Double End
Triple Double End
Quadruple Double Triple End
Output:
4
6
24
So, the first sentence Double Double means 2*2 and Triple Double means 3*2 and so on.
The word End signifies the end of the string.
It looks very simple, but I have no idea how to work with strings and give them a value and continue on from there.
Here is all I have done so far:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int num_of_orders,i,j;
char orders[25];
char str1[25] = "Double";
char str2[25] = "Triple";
char str3[25] = "Quadruple";
scanf("%d", &num_of_orders);
for (i=0; i<num_of_orders+1; i++){
scanf("%s", orders);
}
return 0;
}
There are a number of ways to approach this problem, as indicated by the variety of answers. There is often no one right answer for how to approach a problem in C. The standard library provides a variety of tools that allow you to craft a number of solutions to just about any problem. As long as the code is correct and protects against error, then the choice of which approach to take largely boils down to a question of efficiency. For small bits of example code, that is rarely a consideration.
One approach to take is to recognize that you do not need the first line in your data file (except to read it/discard it to move the file-position-indicator to the start of the first line containing data.)
This allows you to simply use a line-oriented input function (fgets or getline) to read the remaining lines in the file. strtok then provides a simple way to split each line into words (remembering to strip the '\n' or discard the last word in each line). Then it is a small matter of using strcmp to compare each word and multiply by the correct amount. Finally, output the product of the multiplication.
Here is one slightly different approach to the problem. The program will read from the filename given as the first argument (or from stdin by default):
#include <stdio.h>
#include <string.h>
enum { MAXC = 64 };
int main (int argc, char **argv) {
char buf[MAXC] = ""; /* line buffer */
char *delims = " \n"; /* delimiters */
int idx = 0; /* line index */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file pointer */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line */
if (!idx++) continue; /* discard line 1 */
char *p = buf;
size_t len = strlen (p); /* get length */
int prod = 1;
if (len && buf[len-1] == '\n') /* check for '\n' */
buf[--len] = 0; /* remove newline */
printf (" %s", buf); /* output buf before strtok */
/* tokenize line/separate on delims */
for (p = strtok (p, delims); p; p = strtok (NULL, delims))
{ /* make comparson and multiply product */
if (strcmp (p, "Double") == 0) prod *= 2;
if (strcmp (p, "Triple") == 0) prod *= 3;
if (strcmp (p, "Quadruple") == 0) prod *= 4;
}
printf (" = %d\n", prod); /* output product */
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
Use/Output
$ ./bin/dbltrpl <../dat/dbltrpl.txt
Double Double End = 4
Triple Double End = 6
Quadruple Double Triple End = 24
Look it over and let me know if you have questions.
When it comes to reading the input, you can use strtok with a " " as a parameter to delimite the words you're reading from the input. This is a function filling all of the words read on the input into an array of strings:
PARAMETERS:
char **words: array of strings where you will store all of the words read in the input
char *input: the input you read (i.e. "Double Double end")
char *s: the delimiter you'll use to read words in the input (i.e. " ", "\n")
void getWords(char **words, char *input, char *s){
*words = strtok(str, s);
while(*words){
words++;
*words = strtok(NULL, s);
}
words++;
*words=NULL; //last element will point to NULL
}
Once you have read the words from the input, and filled them inside an array of strings, you could do something like this to calculate the output:
int calculate(char **words){
int result = 1;
while(*words){
if (strcmp(*words, "Quadruple") == 0){
result *= 4;
}else if (strcmp(*words, "Triple") == 0){
result *= 3;
}else if (strcmp(*words, "Double") == 0){
result *= 2;
}else if (strcmp(*words, "End") == 0){
return result;
}
words++;
}
}
Note that you need to correctly initialize the parameters you're passing before calling those functions. Otherwise, it may cause a Segmentation Fault.
You will have to use the methods from the string.h library, such as: strcmp(to compare two strings), strcpy(to copy one string to another) etc. which are generally used when dealing with strings manipulation in c.
Since, we do not know the size of the results array at compile time, we will have to allocate memory to it dynamically. For this purpose I have used malloc and free.
Here is the code to do that:
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
int main()
{
int num_of_orders, i, j;
char orders[25];
char str1[25];
strcpy(str1,"Double");
char str2[25];
strcpy(str2,"Triple");
char str3[25];
strcpy(str3,"Quadruple");
scanf("%d", &num_of_orders);
getchar();
int *results = malloc(num_of_orders*sizeof(int));
for (i=0; i < num_of_orders; i++)
{
results[i] = 1;
strcpy(orders,"");
while(strcmp(orders,"End") != 0)
{
scanf("%s", orders);
getchar();
if(strcmp(orders,str1)==0)
results[i] *= 2;
else if(strcmp(orders,str2) == 0)
results[i] *= 3;
else if(strcmp(orders,str3)==0)
results[i] *= 4;
}
}
for(i = 0; i < num_of_orders; i++)
printf("%d\n", results[i]);
free(results);
return 0;
}
Note: This program uses strcmp, which does case-sensitive comparison. If you want case-insensitive comparison, then use strcasecmp instead.
Don't forget the fact that the multiplication of integers is commutative:
#include <stdio.h>
#include <string.h>
int main(void)
{
int num_of_orders, i;
char orders[25];
int result;
char *ptr;
scanf("%d", &num_of_orders);
getchar(); // To comsume '\n'
for (i = 0; i < num_of_orders; i++)
{
fgets(orders, sizeof orders, stdin);
result = 1;
ptr = orders;
while(ptr = strstr(ptr, "Double"))
{
result *= 2;
ptr++;
}
ptr = orders;
while(ptr = strstr(ptr, "Triple"))
{
result *= 3;
ptr++;
}
ptr = orders;
while(ptr = strstr(ptr, "Quadruple"))
{
result *= 4;
ptr++;
}
printf("%d\n", result);
}
return 0;
}
What a trivial approach!
Note that strtok() is destructive, namely it will modify order, which can cause some problems if you want to use it later. Also, I think programs using strtok() are less readable. So it might be better to avoid it when possible.
I have worked on Python and I know that to concatenate a string to be --- you can simply "multiply" an integer by the char "-", so in this case we can simply do it like result=3*"-". I am stuck on trying to do this in C language.
How can I do this in C, for example:
#include <stdio.h>
int main (void)
{
int height=0;
int n=0;
char symbol='#';
printf("Height: ");
scanf("%d",&height);
n=height+1;
while (n>=2)
{
printf("symbol*n");
n=n-1;
}
return 0;
}
So it prints an inverted pyramid for height=5:
#####
####
###
##
#
Thank you in advance!!
There isn't a built-in way to repeat the output like that. You have to code it yourself.
void multiputchar(char c, size_t count)
{
for (int i = 0; i < count; i++)
putchar(c);
}
For a library function, you might care about whether putchar() fails, so you might be better to write:
int multiputchar(char c, size_t count)
{
for (int i = 0; i < count; i++)
{
if (putchar(c) == EOF)
return(EOF);
}
return (unsigned char)c;
}
But if the return value will always be ignored, the first is simpler. The cast is necessary to ensure that if your char type is signed, you can tell the difference between a failure and successful output of ΓΏ (y-umlaut, U+00FF, LATIN SMALL LETTER Y WITH DIAERESIS, 0xFF in 8859-1 and related code sets).
In C, you also need to handle the memory you use. So if you wanted a "---" string, you would also need to allocate space for that string. Once allocated the space, you would fill it with the given character.
And afterwards, you'd have to free the area.
So:
char *charmul(char c, int n)
{
int i;
char *buffer; // Buffer to allocate
buffer = malloc(n+1); // To store N characters we need N bytes plus a zero
for (i = 0; i < n; i++)
buffer[i] = c;
buffer[n] = 0;
return buffer;
}
Then we'd need to add error checking:
char *charmul(char c, int n)
{
int i;
char *buffer; // Buffer to allocate
buffer = malloc(n+1); // To store N characters we need N bytes plus a zero
if (NULL == buffer)
return NULL;
for (i = 0; i < n; i++)
buffer[i] = c;
buffer[n] = 0;
return buffer;
}
Your source would become:
#include <stdio.h>
// charmul here
int main (void)
{
int height=0;
int n=0;
char symbol='#';
printf("Height: ");
scanf("%d",&height);
n=height+1;
while (n>=2)
{
char *s;
s = charmul(symbol, n);
printf("%s\n", s);
free(s); s = NULL;
n=n-1;
}
return 0;
}
An alternative implementation would seek to reduce the number of malloc's, to enhance performance. To do so you'd need to also pass to the function a pointer to the previous buffer, which, if shorter, could be recycled with no need for a further malloc, and if longer, would be free'd and reallocated (or one could use realloc). You would then do a free() only of the last nonrecycled value:
char *charmul_recycle(char c, int n, char *prevbuf)
{
int i;
if (prevbuf && (n > strlen(*prevbuf)))
{
free(prevbuf); prevbuf = NULL;
}
if ((NULL == prevbuf)
{
prevbuf = malloc(n+1); // To store N characters we need N bytes plus a zero
if (NULL == prevbuf)
return NULL;
}
for (i = 0; i < n; i++)
prevbuf[i] = c;
prevbuf[n] = 0;
return prevbuf;
}
char *buffer = NULL;
while(n > 2)
{
buffer = charmul_recycle(symbol, n, buffer);
if (NULL == buffer)
{
fprintf(stderr, "out of memory\n");
abort();
}
printf("%s\n", buffer);
n--;
}
Of course the whole thing can be done with a single straight allocation and a progressive shortening of the string (by placing s[n] to be zero), but then we wouldn't be using the "generating multiple character" features:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (void)
{
char *string;
int height=0;
int n=0;
char symbol='#';
printf("Height: ");
scanf("%d",&height);
n=height+1;
string = malloc(n); // allocate just enough memory (n is height+1)
memset(string, symbol, n); // fill string with symbols
while (--n) // with n ever decreasing...
{
string[n] = 0; // Truncate string after n characters
printf("%s\n", string); // output string
}
free(string); // string = NULL; // finally free the string
return 0;
}
Update (thanks to Jonathan Leffler): above, there's a potentially dangerous "overoptimization". Immediately before each use of string, string is correctly zero-terminated ("string[n] = 0;"). But it remains true that I have allocated a string variable and filled it with stuff, and did not immediately zero-terminate it. In the above code, it all works out perfectly. It's still bad coding practice, because if the code was reused and the cycle removed, and the string used for some other purpose (which in this case is unlikely, but still...), the nonterminated string might become a subtle bug.
The quickest fix is to slap a termination after the allocation:
string = malloc(n); // allocate just enough memory (n is height+1)
memset(string, symbol, n-1); // fill string with symbols
string[n-1] = 0; // zero-terminate string
I've now wandered far from the original topic, but this would mean that in this instance the string is correctly zero-terminated twice. To avoid this, the code can be rewritten into a "cut and paste safe" version, also more clearly showing the extra zero as addition to n:
n=height; // Number of characters
string = malloc(n+1); // Allocate memory for characters plus zero
memset(string, symbol, n); // Store the characters
string[n] = 0; // Store the zero
while (n) // While there are characters
{
printf("%s\n", string); // Print the string
string[--n] = 0; // Reduce it to one character less than before
}
The cycle now accepts any valid string with meaningful n, and if it is removed, the string is left in a useable state.