Using 2D arrays, my arrays don't appear - c

My programm compiles, and it is ok. When I start it and type "AB", they appear on one line, but when I type "BAAB", the "AB" goes under the "BA". I am stuck here for a long time. I will be very glas if you give a solution for this problem.
//it needs to read input from the keyboard and appear every letter constructed by astersks
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
char line[100] = { 'A', 'B' };
int i;
int row;
int col;
bool A[7][6] = { {0, 0, 1, 0, 0, 0}, //letter A
{1, 0, 0, 0, 1, 0},
{1, 0, 0, 0, 1, 0},
{1, 1, 1, 1, 1, 0},
{1, 0, 0, 0, 1, 0},
{1, 0, 0, 0, 1, 0},
{1, 0, 0, 0, 1, 0} };
bool B[7][6] = { {1, 1, 1, 0, 0, 0}, //letter B
{1, 0, 0, 1, 0, 0},
{1, 0, 0, 1, 0, 0},
{1, 1, 1, 1, 0, 0},
{1, 0, 0, 0, 1, 0},
{1, 0, 0, 0, 1, 0},
{1, 1, 1, 1, 0, 0} };
int main () {
//reads the input
printf ("Type a word or number, or both: ");
scanf ("%s", line);
// loop for every row of every letter
for (row = 0; row < 7; row++)
{
// loop for every inputed letter
for (i = 0; line[i]; i++)
{
// if letter is A do this...
if (line[i] == 'A')
{
// lopp for every column to read the asterisks
for (col = 0; col < 5; col++) {
if (A[row][col])
printf ("*");
else
printf (" ");
}
printf ("\n");
}
if (line[i] == 'B') {
for (col = 0; col < 5; col++) {
if (B[row][col])
printf ("*");
else
printf (" ");
}
}
}
}
return (0);
}

Trex, what you first need to understand is "What does my code do if I take NO input?". So comment out your initial printf and scanf and check:
//reads the input
// printf ("Type a word or number, or both: ");
// scanf ("%s", line);
Output:
$ ./bin/multi_array_mess
*
*** * *
* * * *
* * *****
**** * *
* ** *
* ** *
****
What values of line produce that output? {'A', 'B'} What happens if line contains anything other than AB? Try just 'A':
$ Type a word or number, or both: A
*
* *
* *
*****
* *
* *
* *
Then with 'B':
$ ./bin/multi_array_mess
Type a word or number, or both: B
*** * * * * **** * ** *****
It should be obvious that a newline is missing somewhere in the loop that prints B. Changing your logic to match the way you print 'A' similar to:
if (line[i] == 'B') {
for (col = 0; col < 5; col++) {
if (B[row][col])
printf ("*");
else
printf (" ");
}
printf ("\n");
}
Provides the desired output if you enter just 'B' in response to your prompt:
$ ./bin/multi_array_mess
Type a word or number, or both: B
***
* *
* *
****
* *
* *
****
What is printed if you enter anything other than 'A' or 'B'? NOTHING. What happens if you enter both 'A' and 'B'? (you get a mess).
So it looks just from tinkering with your program, it was designed to print either A of B using '*' as the character depending on the values of 0 or 1 in your arrays. You can either fix the logic to handle line containing both 'A' and 'B', or you can limit your input to one character at a time.
Since your intent appears to be able to handle multiple characters in line at a time, what happens if we just tweak the loop logic a bit. E.g.:
// loop for every inputed letter
for (i = 0; line[i]; i++)
{
// loop for every row of every letter
for (row = 0; row < 7; row++)
{
// if letter is A do this...
if (line[i] == 'A')
{
Give it a try...
Next, think about how you are prompting for input:
printf ("Type a word or number, or both: ");
Wouldn't it make more sense to limit your request to what your code will provide output for?
printf ("Please enter A or B, or both: ");
After receiving input, wouldn't it more sense to check what your user provided before blindly passing the input to the rest of your program? Something simple will do:
for (i = 0; i < (int)strlen (line); i++)
if (line[i] != 'A' || line[i] != 'B') {
fprintf (stderr, "error: invalid input '%c'.\n", line[i]);
return 1;
}
Now try your program again.
Printing Each Letter on One Row
OK. Now that I know what you are intending, it makes it a bit easier to help. In order to print each array as '*' or ' ' on the same row, you basically have to loop through each row, checking if a given letter is specified in line and print that row for every character you need to print. You can cut down on the code repetition by creating a function to print the row for each array (you can actually use a macro, but we'll leave that for later).
In keeping with the earlier discussion, I added a few sanity checks and created a new character array allowed that holds a list of the current characters you have created arrays for (the allowed characters). Just add to it as you add arrays. You will also note I moved all your global variables inside main. There is nothing wrong with using globals, but use them sparingly, and only when necessary.
Putting the pieces together, you could do something like:
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#define MAXL 100
#define ROWS 7
#define COLS 5
void prn_letter_row (bool a[][COLS+1], size_t row);
int main (void) {
char line[MAXL] = {0};
char allowed[] = "AB";
size_t i, row, len;
i = row = len = 0;
bool A[ROWS][COLS+1] = {{0, 0, 1, 0, 0, 0}, //letter A
{1, 0, 0, 0, 1, 0},
{1, 0, 0, 0, 1, 0},
{1, 1, 1, 1, 1, 0},
{1, 0, 0, 0, 1, 0},
{1, 0, 0, 0, 1, 0},
{1, 0, 0, 0, 1, 0}};
bool B[ROWS][COLS+1] = {{1, 1, 1, 0, 0, 0}, //letter B
{1, 0, 0, 1, 0, 0},
{1, 0, 0, 1, 0, 0},
{1, 1, 1, 1, 0, 0},
{1, 0, 0, 0, 1, 0},
{1, 0, 0, 0, 1, 0},
{1, 1, 1, 1, 0, 0}};
/* prompt for input */
while (!*line)
{
printf ("\nType a word or number, or both [A-B only]: ");
if (!fgets (line, MAXL, stdin))
fprintf (stderr, "error: no input provided - 'ctrl+d'.\n");
len = strlen (line); /* get length, remove trailing \n */
if (line[len-1] == '\n')
line[--len] = 0;
char *p = line;
while (*p) { /* check each character against 'allowed' */
if (!strchr (allowed, *p)) {
fprintf (stderr, "error: invalid character '%c'.\n", *p);
*line = 0;
}
p++;
}
}
printf ("valid: %s\n\n", line);
for (row = 0; row < ROWS; row++) {
for (i = 0; i < len; i++) {
switch (line[i]) {
case 'A' : prn_letter_row (A, row);
break;
case 'B' : prn_letter_row (B, row);
break;
}
}
putchar ('\n');
}
return 0;
}
/* print given row for letter */
void prn_letter_row (bool a[][COLS+1], size_t row)
{
size_t i;
putchar (' '); /* provide a space before each letter */
for (i = 0; i < COLS; i++)
if (a[row][i])
putchar ('*');
else
putchar (' ');
}
Use/Output
$ ./bin/chars_from_arrays
Type a word or number, or both [A-B only]: A
valid: A
*
* *
* *
*****
* *
* *
* *
$ ./bin/chars_from_arrays
Type a word or number, or both [A-B only]: B
valid: B
***
* *
* *
****
* *
* *
****
$ ./bin/chars_from_arrays
Type a word or number, or both [A-B only]: AB
valid: AB
* ***
* * * *
* * * *
***** ****
* * * *
* * * *
* * ****

Here is your fixed code
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
char line[100] = {'A','B'};
int i;
int row;
int col;
// Support only latin aphabet, easly expandable
bool chars[26][7][6] = {
{ {0,0,1,0,0,0 }, //letter A
{1,0,0,0,1,0 },
{1,0,0,0,1,0 },
{1,1,1,1,1,0 },
{1,0,0,0,1,0 },
{1,0,0,0,1,0 },
{1,0,0,0,1,0 }},
{ {1,1,1,0,0,0 }, //letter B
{1,0,0,1,0,0 },
{1,0,0,1,0,0 },
{1,1,1,1,0,0 },
{1,0,0,0,1,0 },
{1,0,0,0,1,0 },
{1,1,1,1,0,0 }},
// to Z
};
int main()
{
printf ("Type a word or number, or both: ");
scanf ("%s",line);
//reads the input
for (row = 0;row<7;row++)
// loop for every row of every letter
{
for (i = 0; line[i]; i++) //loop for every inputed letter
{
for (col = 0;col < 6;col++) // lopp for every column to read the asterisks
{
if (chars[toupper(line[i]) - 'A'][row][col])
{
printf("*");
}
else
printf(" ");
}
}
printf("\n");
}
return (0);
}
You can add multiple character support by forming 3d array like
bool chars[26][7][6] = {
{ {0,0,1,0,0,0 }, //letter A
{1,0,0,0,1,0 },
{1,0,0,0,1,0 },
{1,1,1,1,1,0 },
{1,0,0,0,1,0 },
{1,0,0,0,1,0 },
{1,0,0,0,1,0 }},
{ {1,1,1,0,0,0 }, //letter B
{1,0,0,1,0,0 },
{1,0,0,1,0,0 },
{1,1,1,1,0,0 },
{1,0,0,0,1,0 },
{1,0,0,0,1,0 },
{1,1,1,1,0,0 }},
// to Z
};
Than access to array member
chars[toupper(line[i]) - 'A'][row][col];
in this case you may not use any character if checks only for loops
this will only works with small string sizes, because standard console support up to 80 characters in length, to deal with it you can setup console width, or perform some manipulation with console cursors to break out formed string properly (like SetConsoleCursorPosition) in Windows.
if you want to support all printable chars, your 3d array should start at whitespace char up to 128 character. For more info look to ASCII character table.

Related

struct field changed but it isn't expected

I'm trying to translate a sentence into morse code. I have a morse.txt file with this type of content :
'-','-','-','-','-',0, /* Caractère 0 */
'.','-','-','-','-',0, /* Caractère 1 */
'.','.','-','-','-',0, /* Caractère 2 */
In order to translate each letter from the sentence I thought about create a 2 dimension array in which we would find a letter and its translation in morse. Here's my struct
typedef struct
{
char lettre;
char *morse;
} t_morse;
I created a function returning that array :
t_morse *loadTab()
{
t_morse *tabMorse = malloc(LETTER_NUMBER * sizeof(t_morse));
ssize_t read;
size_t len = 0;
char index = 0, k = 0, j;
char *line = NULL;
FILE *f = fopen("morse.txt", "r");
if (f == NULL)
{
printf("Impossible d'ouvrir morse.txt\n");
exit(0);
}
while (read = getline(&line, &len, f) != -1 && line != NULL)
{
char *morse = malloc(6 * sizeof(char));
char *stringfinal = malloc(50 * sizeof(char));
strcpy(stringfinal, line);
k = 0;
for (j = 0; j < strlen(stringfinal); j++)
if (stringfinal[j] == '-' || stringfinal[j] == '.')
morse[k++] = stringfinal[j];
tabMorse[index].morse = morse;
tabMorse[index].lettre = index + 48;
printf("lettre : %c, morse : %s\n", tabMorse[index].lettre, tabMorse[index].morse);
index++;
free(morse);
free(stringfinal);
}
fclose(f);
for (index = 0; index < LETTER_NUMBER; index++)
{
printf("toto %c : %s\n", tabMorse[index].lettre, tabMorse[index].morse);
}
return tabMorse;
}
But it doesn't as it would be : when executing the programm, the first printf (printf("lettre : %c, morse : %s\n", tabMorse[index].lettre, tabMorse[index].morse);) show me the thing that I want. However after this when I want to iterate through that array and display the structs, the letter field is the right but in the morse field I get a "" string, and I don't know why.
You can see below, a screen of the stdout when launching the program :
Do you know why it acts like that ?
The immediate problem is that you call free(morse); around every iteration of the loop. So when you print it from the second loop the contents are undefined.
You also do not null terminate the morse[] string.
tabMorse[index].morse = morse;
...
free(morse);
Since tabMorse[index].morse is equal to morse, free(morse) is the same as free(tabMorse[index].morse), which is obviously not right.
You do not want to free the chunk of memory that you just stashed a pointer to. You need to keep it allocated so you can safely dereference the pointer later.
As others have noted (no use repeating), you cannot use pointers to heap memory that has been free()'d. (It's best to not duplicate pointers unless you really keep track of them.)
tabMorse[index].morse = morse;
/* 3 lines omitted */
free(morse);
Too much code hides problems such as this.
Take a step back and think about Morse Code. There are only two symbols, and no character is longer than 5 symbols (or is it 6 maximum?).
A byte (unsigned) has 8 bits that can be in one of two states.
Imagine "encoding" the Morse alphabet in the lowest bits of a byte, one byte per character. Because Morse code uses 1 to 5 symbols per character, another bit (on the left of the low bits) can be used as a flag meaning "the remaining bits on my right are to be used as 0=dot and 1=dash"... The Morse letter 'E' (one dot only) would be represented by 0b000000t0, where 't' would be the 'trigger' (always 1), and the lowest bit 0 would signify a single 'dot'.
The familiar "SOS" would be 0b00001000, 0b00001111, 0b00001000 ("... --- ...").
The code below has been adapted from something I wrote for Brail translation. The (incomplete) octal values in the table have NOT been checked as valid Brail but give you a start as to how you might, with the description above, encode 128 characters (7-bit ASCII) in Morse (replacing the Brail encoding). (The tables entries for 'S/s', 'O/o' and 'SP' have been adapted to be correct Morse characters.)
It seems this is a lot less code, and doesn't provide many corners for bugs to lurk.
void morsify( char c ) {
unsigned char oct[] = { // TODO: adapt these values from Brail to Morse
// CTRL codes
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// CTRL codes
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// punctuation
000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 002, 0, 0, 0,
// punctuation & digits
0, 001, 003, 011, 031, 021, 013, 033, 023, 012, 032, 0, 0, 0, 0, 0,
// # + A-O
0, 001, 003, 011, 031, 021, 013, 033, 023, 012, 032, 005, 007, 015, 035, 017,
// P-Z + braces
017, 037, 027, 010, 036, 045, 047, 072, 055, 075, 065, 0, 0, 0, 0, 0,
// ` + a-o
0, 001, 003, 011, 031, 021, 013, 033, 023, 012, 032, 005, 007, 015, 035, 017,
// p-z + braces
017, 037, 027, 010, 036, 045, 047, 072, 055, 075, 065, 0, 0, 0, 0, 0,
};
bool trig = false;
uint8_t morse = oct[ c ]; // fetch copy of bit pattern from table
for( uint8_t i = 0x80; i; i >>= 1 )
if( !trig && (i & morse) ) // keep scanning right until trigger bit encountered
trig = true;
else if( trig ) // output has been triggered
putchar( i & morse ? '-' : '.' ); // 1's = '-', 0's = '.'
putchar( ' ' ); // space between characters
}
int main() {
char str[] = "Stuff happens";
puts( str );
for( int i = 0; str[ i ]; i++ )
morsify( str[ i ] );
putchar( '\n' );
char str1[] = "SOS sos SOS";
puts( str1 );
for( i = 0; str1[ i ]; i++ )
morsify( str1[ i ] );
putchar( '\n' );
return (0);
}
Stuff happens
... ---. ..-.- .-- .-- ..-- --- --- ...- --.- ...
SOS sos SOS
... --- ... ... --- ... ... --- ...
If you want to "buffer-up" the characters, simply replace the putchar() with your buffering scheme. Then you could output one long string instead of each individual dot/dash & SP.

checking array for values and then passing to new array. c

So given an array of:
input[3] = {0, 0, 0}
this outputs :
output[3] = {3, 0 ,0}
code:
void create_hist(double input[], int num_of_inputs, int output[])
{
int num_to_check = input[0];
int counter = 0;
for (int i = 0; i < num_of_inputs; i++)
{
int j = output[i];
if ((int)input[i] == num_to_check)
{
counter++; /* it was found */
}
output[j] = counter;
}
return;
}
but if I have a floating point array
input[5] = {0.0000, 1.0000, 2.0000, 3.0000, 4.000}
and I want to truncate the values to int, and count how many times each integer in the range 0 - 10 appears in the input array then output it to:
output[5] = {1, 1, 1, 1, 1}
output[0] = {1} //indicates how many times 0 appeared in the array
or
input[10] = {1.000, 4.000, 5.0000, 2.000, 4.000, 7.000, 9.000, 6.000, 0.000, 0.000}
and output
output[10] = {2, 1, 1, 0, 2, 1, 1, 1, 0, 1}
output[0] = {2} // this indicates how many times 0 appeared in the first array
Can anyone tell me how to do this?
You shouldn't use output[i] as an array index. It's a counter, not the value whose count you want. You should use (int)input[i] as the index.
You first need to initialize all elements of output to 0, then you increment the elements corresponding to the integer part of each input.
memset(output, 0, sizeof(output[0]) * MAX_INPUT_VALUE);
for (int i = 0; i < num_of_inputs; i++) {
output[(int)input[i]]++;
}

Making sure a user's input matches characters generated by my program

This is for Homework
I'm programming a simplified game of scrabble where I have my program randomly generate characters then the user would try and create a word from those generated characters, then get a score afterwards. The issue I'm having is making sure the user is actually using the characters provided. I have no clue on how to approach this problem. I don't need any code but hints would be appreciated or even links for a point to start at. Thanks for any help!
EDIT - About half my program [The part that creates the letter set]
void generate_letter_set(int letter_set[] , int size_let , int num_let)
{
int arr[N];
const char let[] =
{'K','J','X','Q','Z','B','C','M','P','F','H','V','W','Y','G','L','S','U','D','N','R','T','O','A','I','E'};
const int freq[] =
{ 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 4, 4, 4, 4, 6, 6, 6, 8, 9, 9, 12 };
int score[] =
{ 5, 8, 8, 10, 10, 3, 3, 3, 3, 4, 4, 4, 4, 4, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1};
int index = 0;
for(int i = 0 ; i < 26 ; i++) {
for(int f = 0 ; f < freq[i]; f++) {
arr[index++] = let[i]; //All the 96 letters are stored in let[i]
//printf("%c " , let[i]); // Created the letter bank for all the letters
}
} int letter;
printf("Your letters are: ");
for(int l = 0; l < 7; l++){
letter = rand() % 97;
printf("%c ", arr[letter]);
}
}
There are a lot of different ways to search an array for certain characters. The basis of what you need is a very simple search function.
One simple solution would be to use two nested for loops. Assuming let[] is your 'haystack' to check and word is your user input:
// Check each letter of word[]...
for (int ii = 0; ii <= lengthOfUserInput; ii++)
{
char characterToValidate = word[ii];
// ... for not existing in let[]
for (int jj = 0; jj <= lengthOfStringOfValues; jj++)
{
if (characterToValidate != let[jj])
}
}

How to print binary array as characters

I have an array that holds 28 ints which are all 1's and 0's. However, I need to print this information as 4 characters so how do I get each 7 bytes of data to become one bit in order to print.
Not sure this makes sense so I will illustrate what I need to:
Right now my array (in order) is this: 0101101111011101011000100010
But I need to somehow take those first 7 numbers (0101101) and print that out as Z and do that with the next 7, the next 7...
Thanks for your help!
I think this might be something along the lines you are looking for.
int to_int(int *bits) {
int power = 2;
int digit = 1;
int value = 0;
int i=0;
for(i=0; i <= 6; i++) {
if(bits[i] == 1) {
value += digit;
}
digit *= power;
}
return value;
}
int main() {
int myArray[28] = {0, 1, 0, 1, 1, 0, 1,
1, 1, 1, 0, 1, 1, 1,
0, 1, 0, 1, 1, 0, 0,
0, 1, 0, 0 ,0, 1, 0};
char theChars[5];
theChars[0] = to_char(&myArray[0]);
theChars[1] = to_char(&myArray[7]);
theChars[2] = to_char(&myArray[14]);
theChars[3] = to_char(&myArray[21]);
theChars[4] = '\0';
printf("%s\n",&theChars[0]);
}
Also, I don't think your expected output is correct.
Well, there is always the stupid way:
Cycle through each 7 blocks.
int bytes=7;
for(int i=0; i++;i<4){
double ASCII = 0;
for(int j=0; i++;j<bytes){
ASCII+=Math.pow(2, bytes-j-1)*array[i*bytes + j]
}
char c = (char) ASCII // you'll have some trouble with types here
}
Assuming your input array is called inputBits[] Try something like this:
const int input_bit_count = 28;
char output[input_bit_count / 7];
int outIdx = 0;
// step through the bit stream converting bits to 7-bit characters
for( int inIdx = 0; inIdx < input_bit_count; ){
// shift over and add the next bit to this character
output[outIdx] <<= 1;
if( inputBits[inIdx] != 0 ){
output[outIdx] |= 1;
}
inIdx++;
if( inIdx % 7 == 0)
// after each 7 bits, increment to next output character
outIdx++;
}
// done processing, now print it out
for( int chIdx = 0; chIdx < input_bit_count / 7; chIdx++ ){
printf( "%c", output[chIdx] );
}

Reading Input into Array

I am trying to read a series of 8 integers from a file into an array then display those integers. I keep getting a segmentation fault the third time around, and I can't quite figure out what I am doing wrong.
struct aStruct {
int a;
int b;
...
};
typedef struct aStruct myStruct;
while(fgets(line, MAX_LENGTH, file) != NULL) {
int myArray[8] = {0};
char* val = strtok (line," ,\n\t\r");
while (val != NULL)
{
myArray[i] = atoi(val);
i++;
val = strtok (NULL, " ,\n\t\r");
}
myStruct foo;
foo.a = myArray[0];
foo.b = myArray[1];
...
}
The input file is structured like so:
0, 0, 1, 5, 0, 0, 0, 0
1, 0, 2, 5, 0, 0, 0, 0
2, 0, 3, 5, 0, 0, 0, 0
3, 0, 4, 5, 0, 0, 0, 0
4, 0, 5, 5, 0, 0, 0, 0
When tested with:
printf("myArray[0]: %d ", myArray[0]);
I get an odd output of 0 0
Where I believe it should be 0 1. Am I not initializing something correctly, or is my new syntax incorrect for the struct? I've tried a few different combinations, cant quite figure it out.
I think your problem here is in uninitialized or non reset i variable. Adding i = 0 inside your while-loop might help.
while(fgets(line, MAX_LENGTH, file) != NULL) {
i = 0; // <<< reseting array index
int myArray[8] = {0};
char* val = strtok (line," ,\n\t\r");
while (val != NULL)
{
//...
i++;
}
}

Resources