Related
Update, Hello guys Thank you all for the help, my initial approach was wrong and I did not use ASCII codes at all.
Sorry for the late replay I had a half-day off today and made a new post for the complete code
there is no errors but the prgram is not working proberly ( this is an update of old post )
I wrote the program, and it is working with no errors But it is not giving me the results I wanted
My only problem is when I read a character how to check its ASCII and store it.
#include <stdio.h>
#include <string.h>
int main()
{
char dictionary[300];
char ch, temp1, temp2;
FILE *test;
test=fopen("HW2.txt","r");
for(int i=0;i<2000;i+=1)
{ ch=fgetc(test);
printf("%c",ch);
}
}
If we are talking about plain ASCII, values goes from 0 to 127, your table shoud look like:
int dictionary[128] = {0};
Regarding your question:
how to check its ASCII and store it
Consider a char being a tiny int, they are interchangeable and you don't need any conversion.
fgetc wants an int in order to handle EOF, and trying to read 2000 characters from a file containing less than 2000 bytes can have very bad consequences, to read the whole file:
int c;
while ((c = fgetc(test)) != EOF)
{
if ((c > 0) && (c < 128))
{
dictionary[c]++;
}
}
for (int i = 1; i < 128; i++)
{
if (dictionary[i] > 0)
{
printf("%c appeared %d times\n", i, dictionary[i]);
}
}
EDIT:
Rereading, I see that you want to store words, not chars, ok, then it's a bit more difficult but nothing terrible, do not limit yourself to 300 words, use dynamic memory:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// A struct to hold the words and the
// number of times it appears
struct words
{
size_t count;
char *word;
};
int main(void)
{
FILE *file;
file = fopen("HW2.txt", "r");
// Always check the result of fopen
if (file == NULL)
{
perror("fopen");
exit(EXIT_FAILURE);
}
struct words *words = NULL;
size_t nwords = 0;
char *word = NULL;
size_t nchars = 1;
size_t i;
int c;
// while there is text to scan
while ((c = fgetc(file)) != EOF)
{
if (isspace(c))
{
if (word != NULL)
{
// Search the word in the table
for (i = 0; i < nwords; i++)
{
// Found, increment the counter
if (strcmp(word, words[i].word) == 0)
{
words[i].count++;
free(word);
break;
}
}
// Not found, add the word to the table
if (i == nwords)
{
struct words *temp;
temp = realloc(words, sizeof(*temp) * (nwords + 1));
if (temp == NULL)
{
perror("realloc");
exit(EXIT_FAILURE);
}
words = temp;
words[nwords].word = word;
words[nwords].count = 1;
nwords++;
}
// Prepare the next word
word = NULL;
nchars = 1;
}
}
else
{
char *temp;
temp = realloc(word, nchars + 1);
if (temp == NULL)
{
perror("realloc");
exit(EXIT_FAILURE);
}
word = temp;
word[nchars - 1] = (char)c;
word[nchars++] = '\0';
}
}
for (i = 0; i < nwords; i++)
{
printf("%s appeared %zu times\n", words[i].word, words[i].count);
free(words[i].word);
}
free(words);
fclose(file);
return 0;
}
In C, characters are, essentially, their ASCII code (or rather, their char or unsigned char value). So once you read a character, you have its ASCII code already.
However, fgetc() doesn't always return the character it read for you; it may fail, for which reason it returns an int, not an unsigned char, which will be -1 in case of failure.
So:
You need to define an int variable to take the result of fgetc().
If it's not EOF, you can cast the result back into a unsigned char. That's your character, and it's ASCII value, at the same time.
PS - I'm ignoring non-ASCII characters, non-Latin languages etc. (But C mostly ignores them in its basic standard library functions too.)
Currently working on how to split a .csv file with ",". Then creating a
2-D array to store the Alphabet and the number together. As it stands, the code below outputs: "a,,,,,,,,,,,,,,,,,,,". Also, what is the appropriate data type to declare the 2-D array since the values would be Char and int? Furthermore, I know this is a duplicate question because I've not found previous questions helpful. A simple explanation would be great and appreciated, explanation on how to split the file with this piece code would be perfect "%*[^,]" if possible. Thanks in advance.
Sample contents of the .csv file below.
A,1
B,2
C,3
.....
The program:
char single;
/* char array[26][2]; I was thinking the 2-d array would be declared like that. */
while ((single = fgetc(fpointer)) != EOF)
{
fscanf(fpointer,"%*[^,]");
printf("%c",single);
}
fclose(fpointer);
............................................................
edit code: With strtok() and fgetc()
............................................................
//char single;
char s[26] = ",";
char *token;
char str[100];
while (fgets(str,100,fpointer))
{
while((token = strtok(NULL, s)) != NULL)
{
printf(" %s\n", token);
}
}
fclose(fpointer);
typedef struct
{
char charVal;
int intVal;
}SplitValue;
SplitValue result[50];
int count = 0;
FILE *myFile = NULL;
fopen_s(&myFile, "mycsvfile.csv", "r");
char single[100];
if (myFile != NULL)
{
while (fgets(single, 100, myFile) != NULL)
{
// store the first char value
result[count].charVal = single[0];
// store the int value as string
char intval[25];
int i = 0;
for (i = 2; single[i] != '\n'; ++i)
{
intval[i - 2] = single[i];
}
intval[i-2] = 0;
// convert the string to int, either using atoi or sscanf
result[count].intVal = atoi(intval);
// get ready for the next item
count++;
}
fclose(myFile);
}
if (count)
{
for (int i = 0; i < count; ++i)
{
printf("Char value: %c and int value: %d\n", result[i].charVal, result[i].intVal);
}
}
Hope this helps!
Try the following solution, considering comments from DYZ and RoadRunner. Hope it helps somehow.
#include <stdio.h>
#include <stdlib.h>
typedef struct charIntPair {
char alpha;
int value;
} charIntPair_t;
#define MAX_ALPHABET_LENGTH 26
charIntPair_t myAlphabet[MAX_ALPHABET_LENGTH];
int alphabetLength = 0;
int main() {
FILE *fp = fopen("mycsvfile.csv","r");
if (!fp)
return 1; // File could not be opened.
char line[100];
for (alphabetLength=0; alphabetLength < MAX_ALPHABET_LENGTH && fgets(line,100,fp); alphabetLength++) {
int elementsRead = sscanf (line,"%c,%d",
&myAlphabet[alphabetLength].alpha,
&myAlphabet[alphabetLength].value);
if (elementsRead < 2) // not a valid char/int-combination?
break;
}
for (int i=0; i<alphabetLength; i++) {
printf("element %d is (%c,%d)\n", i, myAlphabet[i].alpha, myAlphabet[i].value);
}
return 0;
}
It expects that the character is the first element in a line and that it is immediately followed by a ,. The number may have spaces upfront. The following input yields the following output:
A,1
B,2
C, 3
D,15
E,17
=>
element 0 is (A,1)
element 1 is (B,2)
element 2 is (C,3)
element 3 is (D,15)
element 4 is (E,17)
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 get a runtime error when running a C program,
Here is the C source (parsing.h header code a little lower):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "parsing.h"
int main()
{
printf("Enter text seperated by single spaces :\n");
char *a = malloc(sizeof(char)*10);
gets(a);
char **aa = Split(a, ' ');
int k = SplitLen(a, ' ');
int i = 0;
for(;i<k;i++)
{
printf("%s\n", aa[i]);
}
free(a);
free(aa);
return 0;
}
and the parsing.h file:
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
#include <assert.h>
char** Split(char* a_str, const char a_delim)
{
char** result = 0;
int count = 0;
char* tmp = a_str;
char* last_comma = 0;
/* Count how many elements will be extracted. */
while (*tmp)
{
if (a_delim == *tmp)
{
count++;
last_comma = tmp;
}
tmp++;
}
/* Add space for trailing token. */
count += last_comma < (a_str + strlen(a_str) - 1);
/* Add space for terminating null string so caller
knows where the list of returned strings ends. */
count++;
result = malloc(sizeof(char*) * count);
if (result)
{
size_t idx = 0;
char* token = strtok(a_str, ",");
while (token)
{
assert(idx < count);
*(result + idx++) = strdup(token);
token = strtok(0, ",");
}
assert(idx == count - 1);
*(result + idx) = 0;
}
return result;
}
int SplitLen(char *src, char sep)
{
int result = 0;
int i;
for(i = 0; i<strlen(src); i++)
{
if(src[i] == sep)
{
result += 1;
}
}
return result;
}
I'm sure most of the code is unneeded but I posted the whole lot in case there is some relevance, Here is the runtime error:
a.out: parsing.h:69: Split: Assertion `idx == count - 1' failed.
Aborted
Thanks in advance and for info I didn't program the whole lot but took some pieces from some places but most is my programming Thanks!.
The purpose of the assert function is that is will stop your program if the condition passed as an argument is false. What this tells you is that when you ran your program, idx != count - 1 at line 69. I didn't take the time to check what import that has on the execution of your program, but apparently (?) idx was intended to equal count - 1 there.
Does that help?
There are many problems. I'm ignoring the code split into two files; I'm treating it as a single file (see comments to question).
Do not use gets(). Never use gets(). Do not ever use gets(). I said it three times; it must be true. Note that gets() is no longer a Standard C function (it was removed from the C11 standard — ISO/IEC 9899:2011) because it cannot be used safely. Use fgets() or another safe function instead.
You don't need to use dynamic memory allocation for a string of 10 characters; use a local variable (it is simpler).
You need a bigger string — think about 4096.
You don't check whether you got any data; always check input function calls.
You don't free all the substrings at the end of main(), thus leaking memory.
One major problem the Split() code slices and dices the input string so that SplitLen() cannot give you the same answer that Split() does for the number of fields. The strtok() function is destructive. It also treats multiple adjacent delimiters as a single delimiter. Your code won't account for the difference.
Another major problem is that you analyze the strings based on the delimiter passed into the Split() function, but you use strtok(..., ',') to actually split on commas. This is more consistent with the commentary and names, but totally misleading to you. This is why your assertion fired.
You don't need to include <malloc.h> unless you are using the extra facilities it provides. You aren't, so you should not include it; <stdlib.h> declares malloc() and free() perfectly well.
This code works for me; I've annotated most of the places I made changes.
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int altSplitLen(char **array);
static char **Split(char *a_str, const char a_delim);
static int SplitLen(char *src, char sep);
int main(void)
{
printf("Enter text separated by single spaces:\n");
char a[4096]; // Simpler
if (fgets(a, sizeof(a), stdin) != 0) // Error checked!
{
char **aa = Split(a, ' ');
int k = SplitLen(a, ' ');
printf("SplitLen() says %d; altSplitLen() says %d\n", k, altSplitLen(aa));
for (int i = 0; i < k; i++)
{
printf("%s\n", aa[i]);
}
/* Workaround for broken SplitLen() */
{
puts("Loop to null pointer:");
char **data = aa;
while (*data != 0)
printf("[%s]\n", *data++);
}
{
// Fix for major leak!
char **data = aa;
while (*data != 0)
free(*data++);
}
free(aa); // Major leak!
}
return 0;
}
char **Split(char *a_str, const char a_delim)
{
char **result = 0;
size_t count = 0;
char *tmp = a_str;
char *last_comma = 0;
/* Count how many elements will be extracted. */
while (*tmp)
{
if (a_delim == *tmp)
{
count++;
last_comma = tmp;
}
tmp++;
}
/* Add space for trailing token. */
count += last_comma < (a_str + strlen(a_str) - 1);
/* Add space for terminating null string so caller
knows where the list of returned strings ends. */
count++;
result = malloc(sizeof(char *) * count);
if (result)
{
char delim[2] = { a_delim, '\0' }; // Fix for inconsistent splitting
size_t idx = 0;
char *token = strtok(a_str, delim);
while (token)
{
assert(idx < count);
*(result + idx++) = strdup(token);
token = strtok(0, delim);
}
assert(idx == count - 1);
*(result + idx) = 0;
}
return result;
}
int SplitLen(char *src, char sep)
{
int result = 0;
for (size_t i = 0; i < strlen(src); i++)
{
if (src[i] == sep)
{
result += 1;
}
}
return result;
}
static int altSplitLen(char **array)
{
int i = 0;
while (*array++ != 0)
i++;
return i;
}
Sample run:
$ parsing
Enter text separated by single spaces:
a b c d e f gg hhh iii jjjj exculpatory evidence
SplitLen() says 0; altSplitLen() says 12
Loop to null pointer:
[a]
[b]
[c]
[d]
[e]
[f]
[gg]
[hhh]
[iii]
[jjjj]
[exculpatory]
[evidence
]
$
Note that fgets() keeps the newline and gets() does not, so the newline was included in output. Note also how the printf() printing the data showed the limits of the strings; that is enormously helpful on many occasions.
I have a text file as data.txt and I want to delete the last members of each line:
Here's the text file:
2031,2,0,0,0,0,0,0,54,0,
2027,2,0,0,0,0,0,0,209,0,
2029,2,0,0,0,0,0,0,65,0,
2036,2,0,0,0,0,0,0,165,0,
I would like to delete so it becomes:
2031,2,0,0,0,0,0,0,
2027,2,0,0,0,0,0,0,
2029,2,0,0,0,0,0,0,
2036,2,0,0,0,0,0,0,
I'm working in C but as the numbers can have two or three digits, I'm not sure how to do this.
A couple of uses of strrchr() can do the job:
#include <string.h>
void zap_last_field(char *line)
{
char *last_comma = strrchr(line, ',');
if (last_comma != 0)
{
*last_comma = '\0';
last_comma = strrchr(line, ',');
if (last_comma != 0)
*(last_comma + 1) = '\0';
}
}
Compiled code that seems to work. Note that given a string containing a single comma, it will zap that comma. If you don't want that to happen, then you have to work a little harder.
Test code for zap_last_field()
#include <string.h>
extern void zap_last_field(char *line);
void zap_last_field(char *line)
{
char *last_comma = strrchr(line, ',');
if (last_comma != 0)
{
*last_comma = '\0';
last_comma = strrchr(line, ',');
if (last_comma != 0)
*(last_comma + 1) = '\0';
}
}
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *line = malloc(4096);
if (line != 0)
{
while (fgets(line, 4096, stdin) != 0)
{
printf("Line: %s", line);
zap_last_field(line);
printf("Zap1: %s\n", line);
}
free(line);
}
return(0);
}
This has been vetted with valgrind and is OK on both the original data file and the mangled data file listed below. The dynamic memory allocation is there to give valgrind the maximum chance of spotting any problems.
I strongly suspect that the core dump reported in a comment happens because the alternative test code tried to pass a literal string to the function, which won't work because literal strings are not generally modifiable and this code modifies the string in situ.
Test code for zap_last_n_fields()
If you want to zap the last couple of fields (a controlled number of fields), then you'll probably want to pass in a count of the number of fields to be zapped and add a loop. Note that this code uses a VLA so it requires a C99 compiler.
#include <string.h>
extern void zap_last_n_fields(char *line, size_t nfields);
void zap_last_n_fields(char *line, size_t nfields)
{
char *zapped[nfields+1];
for (size_t i = 0; i <= nfields; i++)
{
char *last_comma = strrchr(line, ',');
if (last_comma != 0)
{
zapped[i] = last_comma;
*last_comma = '\0';
}
else
{
/* Undo the damage wrought above */
for (size_t j = 0; j < i; j++)
*zapped[j] = ',';
return;
}
}
zapped[nfields][0] = ',';
zapped[nfields][1] = '\0';
}
#include <stdio.h>
int main(void)
{
char line1[4096];
while (fgets(line1, sizeof(line1), stdin) != 0)
{
printf("Line: %s", line1);
char line2[4096];
for (size_t i = 1; i <= 3; i++)
{
strcpy(line2, line1);
zap_last_n_fields(line2, i);
printf("Zap%zd: %s\n", i, line2);
}
}
return(0);
}
Example run — using your data.txt as input:
Line: 2031,2,0,0,0,0,0,0,54,0,
Zap1: 2031,2,0,0,0,0,0,0,54,
Zap2: 2031,2,0,0,0,0,0,0,
Zap3: 2031,2,0,0,0,0,0,
Line: 2027,2,0,0,0,0,0,0,209,0,
Zap1: 2027,2,0,0,0,0,0,0,209,
Zap2: 2027,2,0,0,0,0,0,0,
Zap3: 2027,2,0,0,0,0,0,
Line: 2029,2,0,0,0,0,0,0,65,0,
Zap1: 2029,2,0,0,0,0,0,0,65,
Zap2: 2029,2,0,0,0,0,0,0,
Zap3: 2029,2,0,0,0,0,0,
Line: 2036,2,0,0,0,0,0,0,165,0,
Zap1: 2036,2,0,0,0,0,0,0,165,
Zap2: 2036,2,0,0,0,0,0,0,
Zap3: 2036,2,0,0,0,0,0,
It also correctly handles a file such as:
2031,0,0,
2031,0,
2031,
2031
,