Changing a Char to another specified Char in a specified position structure - c

I need to change a specified characters eg: a->z, d->b in a string. for eg: input is abandoned; output is zbznboneb. This is my code.
typedef struct {
char source;
char code;
} Rule;
void encodeChar (Rule table[5], char *s, char *t);
int main ()
{
char s[80], t[80];
Rule table[5] = { 'a', 'd', 'b', 'z', 'z', 'a', 'd', 'b', '\0', '\0'
};
printf ("Source string: \n");
gets (s);
encodeChar (table, s, t);
printf ("Encoded string: %s\n", t);
return 0;
}
void encodeChar (Rule table[5], char *s, char *t)
{
int j = 0;
while (s[j] != '\0') {
if (s[j] == 'a')
putchar ('d');
if (s[j] == 'b')
putchar ('z');
if (s[j] == 'z')
putchar ('a');
j++;
}
return 0;
}
But it gives me output dz, only and does not return the whole word when, i type abandoned.

In addition to, and in agreement with, the other answer, there are fundamentals you are missing.
When you get into encodeChar, what parameter are you using to tell you how many elements table has? (you do want to iterate over each element in table checking table[i].source against each character to determine if a substitution is needed, right?)
note: C generally uses all lowercase for variable and function names while reserving all uppercase for constants and macros. C avoids the use of camelCase and MixedCase names -- leave those for C++ or java. While it is a matter of style, so it is largely up to you, it does say a lot about your appreciation for C, just like using gets does....
Don't use magic numbers in your code. If you need a constant, (e.g. 80), declare one at the top of your code, e.g.
#define MAXC 80 /* maximum characters for input buffer */
If you have multiple constants to declare, using an enum is an orderly way to declare global constants.
Using constants prevents having to pick through multiple array declarations to change their size. There is one convenient place up top to make the change.
Don't use gets, it is helpless against buffer overrun and has been removed from C11. Use fgets. All valid line-oriented input functions (e.g. fgets, and POSIX getline) read and include the trailing '\n' in the buffers they fill with input. Therefore, you need to trim the trailing newline from input, or you will have '\n' dangling off the end of any string you store that can cause problems with comparisons, etc.. Simply get the length and check character length - 1 to validate it is a '\n', then just overwrite it with a nul-terminating character ('\0' or 0, they are equivalent)
It's quite simple, e.g.:
len = strlen (s); /* get length of s */
if (len && s[len - 1] == '\n') /* check for \n */
s[--len] = 0; /* overwrite with \0 */
note: by using --len you now have the new length preserved in len.
Finally, for your encodechar function, you need to know how many elements of table you have. For each character in s you will compare it to each table[i].source if a match is found, then you will assign table[i].code to t and go to the next character. If not found, you will simply assign the character in s to t.
note: there is no need for the 5th element of table (e.g. '\0', '\0'), you can easily nul-terminate t without it -- and it is not a replacement.
Putting that together, you could write encodechar similar to the following:
void encodechar (rule *table, size_t sz, char *s, char *t)
{
size_t i;
while (*s) { /* for each character */
int replaced = 0; /* replaced flag */
for (i = 0; i < sz; i++) /* for each element of table */
if (*s == table[i].source) { /* is char == table[i].source */
*t = table[i].code; /* replace it */
replaced = 1; /* set replaced flag */
break; /* get next character */
}
if (!replaced) /* if not replaced */
*t = *s; /* copy from s to t */
s++, t++; /* increment s and t */
}
*t = 0; /* nul-terminate t */
}
Putting it altogether, and noting main() is type int and therefore returns a value (See: C11 Standard ยง5.1.2.2.1 Program startup (draft n1570). See also: See What should main() return in C and C++?), you can do something similar to the following:
#include <stdio.h>
#include <string.h>
#define MAXC 80 /* maximum characters for input buffer */
typedef struct {
char source;
char code;
} rule;
void encodechar (rule *table, size_t sz, char *s, char *t);
int main (void) {
char s[MAXC] = "", t[MAXC] = "";
rule table[] = { {'a', 'd'}, {'b', 'z'}, {'z', 'a'}, {'d', 'b'} };
size_t len = 0, n = sizeof table/sizeof *table;
printf ("Source string : ");
if (!fgets (s, MAXC, stdin)) {
fprintf (stderr, "error: invalid input.\n");
return 1;
}
len = strlen (s); /* get length of s */
if (len && s[len - 1] == '\n') /* check for \n */
s[--len] = 0; /* overwrite with \0 */
encodechar (table, n, s, t);
printf ("Encoded string: %s\n", t);
return 0;
}
void encodechar (rule *table, size_t sz, char *s, char *t)
{
size_t i;
while (*s) { /* for each character */
int replaced = 0; /* replaced flag */
for (i = 0; i < sz; i++) /* for each element of table */
if (*s == table[i].source) { /* is char == table[i].source */
*t = table[i].code; /* replace it */
replaced = 1; /* set replaced flag */
break; /* get next character */
}
if (!replaced) /* if not replaced */
*t = *s; /* copy from s to t */
s++, t++; /* increment s and t */
}
*t = 0; /* nul-terminate t */
}
Example Use/Output
$ ./bin/encode
Source string : abcdefghijklmnopqrstuvwxyz
Encoded string: dzcbefghijklmnopqrstuvwxya
Always compile with warnings enabled, and do not accept code until it compiles cleanly without warning. To enable warnings add -Wall -Wextra to your gcc compile string. (add -pedantic for several additional warnings). For VS (cl.exe on windoze), add /Wall. For clang, add -Weverything. Read and understand each warning. They will identify any problems, and the exact line on which they occur. You can learn as much about coding by simply listening to what your compiler is telling you as you can from most tutorials.
Look things over and let me know if you have further questions.

If you'd be so kind as to explain where you output any character other than a, z or d within this code:
void encodeChar(Rule table[5], char *s, char *t)
{
int j =0;
while(s[j] != '\0')
{
if (s[j] == 'a')
putchar('d');
if (s[j] == 'b')
putchar('z');
if (s[j]=='z')
putchar('a');
j++;
}
return 0;
}
Please show me where you putchar anything but 'a', 'd' or 'z' and you'll get some strong hints towards the answer to your question.
P.S. Most importantly, if my rather educated guess was correct, YOU NEED A BOOK! You can't safely learn C without one. It's not theoretically impossible, but it is practically unlikely that you'll get it right without wasting heaps of time writing really unstable code.
I recommend K&R2E. It's not beginner friendly in the sense that it assumes you know some other programming concepts, but then again, C is not a beginner friendly language in the sense that you can't just mash your face against a keyboard and win.
If you're looking for something beginner friendly, choose a different language. C doesn't yet have any beginner friendly resources, or any reputable teachers that start from a beginner level, to the best of my knowledge.
If you're going to program in C, it is expected that you be able to read... books, manuals and error messages from compilers and valgrind all need the same kind of attention to detail that your code needs.
Anything you do manage to learn without a book, you're likely going to have to unlearn. So stop guessing immediately, start reading immediately and hopefully next time I hear from you it's not about something basic and explained by the book.

Related

Reading a file and print lines that contain a certain stream of characters decided by the user

Implement a program that receives the name of a text file and a format string via the command line format. A format string is a string that contains only three possible characters: 'V', which indicates a vowel, 'C', indicating a consonant and 'D' indicating a digit.
The program will then have to read the file and print a video the lines that respect the format string.
Note: both uppercase and lowercase characters will be accepted from the format string; assume that the formed string and the lines of the file contain at most 500 characters.
Example
Assuming that the format string is "VCD" and the file contains:
2og
Ok1
Ok
oK2
The program will print Ok1 and oK2 on the screen.
I've come up with this:
int main() {
int x=1, i=0, flag=1, j=0;
char str[500], n_file[100], formato[500],c;
printf("\nInsert file name:\n");
scanf("%s%*c",n_file);
FILE *fr=fopen(n_file, "w");
printf("\nTo quit from writing input 'quit'\n");
while (x!=0) {
fgets(str, sizeof str, stdin);
if (strcmp(str, "quit\n")==0) {
x=0;
}else{
fputs(str,fr);
}
}
fclose(fr);
printf("\nInsert the format string (V for vowels, C for consonants and D for digits):\n");
scanf("%s%*c",formato);
printf("%s",formato);
FILE *fp=fopen(n_file, "r");
while (!feof(fp)) {
fgets(str, sizeof str, fp);
while (str[i]!='\n' && str[i]!='\0' && flag!=0 && formato[j]!='\n') {
printf("\n%c",str[i]);
if (formato[j]=='V') {
if (str[i]=='a' || str[i]=='A' || str[i]=='E' || str[i]=='e' || str[i]=='i' || str[i]=='I' || str[i]=='o' || str[i]=='O' || str[i]=='u' || str[i]=='U') {
flag=1;
i++;
j++;
printf("\nPrimo if");
}else{
flag=0;
}
}else if (formato[j]=='C'){
if ((str[i]>='b'-32 && str[i]<='z'-32) || (str[i])>='b' && str[i]<='z') {
flag=1;
i++;
j++;
printf("\nSecondo if");
}else{
flag=0;
}
}else if (formato[j]=='D') {
if (str[i]>=0 && str[i]<=9) {
flag=1;
i++;
j++;
} else {
flag=0;
printf("\nTerzo if");
}
}
}
if (flag==1) {
printf("\n%s",str);
}
j=0;
i=0;
}
return 0;
}
The idea behind the code is: U compare each character in the format string first with vowels (I'm new to C so it's the only way i thought of doing the comparison, one by one for each vowel, capital or not), then I compare the consonants and then the digits.
If I have a 'V' in the format string but the line does not have a vowel in that position the flag gets set to 0 and thus result in the exit of the 2nd while loop and the str[] line gets replaced with the next file line.
I've put some printfs in the 2nd while and I don't know why the 3rd if sets the flag to 0 even if the format string is "VCD" and I insert a digit as last character.
Alright, this is one of those where I can tell you have put effort and thought into what you are trying to do, you are wisely trying to track the "state" of whether a match or mismatch occurs, and you are using fgets() to read line by line -- but then the train falls completely off the track.
The fact you are supposed to read from the file containing the strings and write to the "screen" makes opening a file to write with "w" and then reading from stdin a bit bewildering (though you could redirect from your file on stdin -- but why?)
But most of all you need to look at Why is while ( !feof (file) ) always wrong? for while (!feof(fp)).
That said, how do you approach a problem like this? You take it step-by-step and think about tackling the following problems:
start by validating the number of arguments is what is needed,
validate the format string is made up of only valid characters 'V', 'C' and/or 'D' (in any case),
open your input file and validate it is open for reading,
loop reading each line from your input file, conditioning your read-loop on the return from your read-function, e.g. while (fgets (line, size, fp)) { ... },
check if the line matches the format string, if so, print it, if not, get the next line (you thought through this part correctly)
One other thought to help keep your logic straight, is to look at the steps and see if it would make sense to write a function to handle the task. This keeps your code tidy and prevents filling main() with an never ending jumble of code that is hard to follow.
For example, if you move the validation checks for if the user provided any bad characters in the format string to it's own function, and move the check whether each line matches the format to it's own function, your main() can be kept readable and easy to follow, e.g.
int main (int argc, char **argv) {
char line[MAXC+1], *fmt, *fname; /* buffer for line, names for args */
FILE *fp; /* file pointer for file stream */
if (argc != NARG + 1) { /* validate 2 arguments given (exe is always 1) */
fprintf (stderr, "error: incorrect number of arguments, 2 required.\n"
"usage: %s str (max 3 chars 'V', 'C', 'D'), fname\n",
argv[0]);
return 1;
}
fname = argv[1]; /* optional descriptive names for cmdline arguments */
fmt = argv[2];
if (bad_fmt_chars (fmt)) { /* validate format string */
return 1;
}
if (!(fp = fopen (fname, "r"))) { /* open/validate file open for reading */
perror ("fopen-fname");
return 1;
}
while (fgets (line, MAXC + 1, fp)) { /* loop reading each line */
line[strcspn (line, "\n")] = 0; /* trim '\n' from line */
if (!bad_line_fmt (line, fmt)) { /* check line format */
puts (line);
}
}
}
Adding the header includes, constants and function declarations used above to the top of the code, you would have a complete program absent the two functions definitions you need to write, e.g.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define NARG 2 /* if you need a constant, #define one (or more) */
#define MAXC 500 /* max chars in line */
#define VALIDC "VCD" /* valid chars for str */
#define VOWELS "AEIOUaeiou" /* vowels */
/* function prototypes (declarations) */
int bad_fmt_chars (const char *str);
int bad_line_fmt (const char *line, const char *fmt);
int main (int argc, char **argv) {
char line[MAXC+1], *fmt, *fname; /* buffer for line, names for args */
FILE *fp; /* file pointer for file stream */
if (argc != NARG + 1) { /* validate 2 arguments given (exe is always 1) */
fprintf (stderr, "error: incorrect number of arguments, 2 required.\n"
"usage: %s str (max 3 chars 'V', 'C', 'D'), fname\n",
argv[0]);
return 1;
}
fname = argv[1]; /* optional descriptive names for cmdline arguments */
fmt = argv[2];
if (bad_fmt_chars (fmt)) { /* validate format string */
return 1;
}
if (!(fp = fopen (fname, "r"))) { /* open/validate file open for reading */
perror ("fopen-fname");
return 1;
}
while (fgets (line, MAXC + 1, fp)) { /* loop reading each line */
line[strcspn (line, "\n")] = 0; /* trim '\n' from line */
if (!bad_line_fmt (line, fmt)) { /* check line format */
puts (line);
}
}
}
Now the two functions. How to check if the user entered any bad characters in the format string. While you can use a nested loop like you started to do, you can also use the strchr() function to replace the inner-loop since it can easily scan your string of allowable (valid) characters to see if the current character is found in it. (you can make it case insensitive by simply converting the current character to upper or lower case consistent with your definition of the valid characters) The use an required header for strchr() can be found in it man-page man 3 strchr1
When writing any function that can succeed or fail, or provides a informational return, choose an appropriate type for the function. For simple true/false returns (good/no-good, etc..), returning a simple int is fine. You can also include stdbool.h and return type bool in that case as well -- up to you.
Using a simple int return, returning 1 to indicate a bad character was found, or 0 if no offending characters were found -- and choosing a name that makes those returns make sense, you could do something like the following the validate the format string, e.g.
/* check if format string has bad char not one of "VCD",
* returns 1 if bad char found, 0 otherwise
*/
int bad_fmt_chars (const char *str)
{
for (int i = 0; str[i]; i++) { /* loop over chars in str */
/* case insensitive char not in VALIDC, return err */
if (!strchr (VALIDC, toupper(str[i]))) {
fprintf (stderr, "error: invalid char '%c' is not one of \"VCD\".\n",
str[i]);
return 1;
}
}
return 0; /* return good (no bad chars) */
}
The function simply scans each character in the format string calling strchr() in a case insensitive way to check that each character is one of the VALIDC characters. It returns 1 immediately if an invalid format character is found, or 0 after scanning all characters.
Checking whether the line meets the format string can be handled in a number of different ways. One of the simplest is just to loop over each character read from the file and then use a switch() statement on the corresponding character from the format string to make sure the current character matches the format character. You can use the case-fallthrough aspect of a switch case: to do that in a case-insensitive way. (any case without a corresponding break falls through to the next case). So if you group both cases for 'V' and 'v' together without a break in the first, you will fall through to the second allowing you just to write the logic to handle a vowel once (same for consonant and digit).
What do you need to do for each character of input? Each character in line will have a corresponding format character in the format string. So for each character, you need to switch on the corresponding format character to determine if the current character is:
a vowel if the format character is 'v' or 'V',
a consonant if the format character is 'c' or 'C', or
a digit if the format character is 'd' or 'D'.
Putting that together and using a mismatch flag to indicate where a mismatch in format is found, you could do something similar to the following:
/* confirm each character in line matches format char in fmt,
* returns 1 on any format mismatch or difference in length,
* returns 0 if format matches exactly.
*/
int bad_line_fmt (const char *line, const char *fmt)
{
int i = 0; /* declare i to survive loop */
/* both line and format chars must be available */
for (; line[i] && fmt[i]; i++) {
int mismatch = 0;
/* case fall-through intentional */
switch (fmt[i]) {
case 'v': /* if not in VOWELS break loop */
case 'V': if (!strchr (VOWELS, line[i])) { mismatch = 1; } break;
case 'c': /* if in VOWELS break loop */
case 'C': if (strchr (VOWELS, line[i]) ||
isdigit (line[i]) ||
ispunct (line[i]) ||
isspace (line[i])) {
mismatch = 1;
}
break;
case 'd': /* if not digit break loop */
case 'D': if (!isdigit (line[i])) { mismatch = 1; } break;
}
if (mismatch) { /* if mismatch between format and char */
return 1; /* return bad format */
}
}
if (line[i] || fmt[i]) { /* if either line or fmt not at end */
return 1; /* return bad format */
}
return 0; /* return line matches format - return good */
}
(note: thanks to #Fe2O3 for finding the cases to 'c' and 'C' that I overlooked -- providing the point the extra eyes are always a benefit)
That is the completed program. You also always compile with Full Warnings Enable which means the compiler will complain about the case fallthrough. The compiler has an option to eliminate that warning (you telling the compiler, yes, case fallthrough was intentional). For gcc that option is -Wno-implicit-fallthrough. That allows you to compile your program with no warnings. Never accept code until it compiles without warning. Setting -Werror tells the compiler to treat warnings as errors (an easy way to keep yourself honest)
Example Use/Output
Does it work? With your input in the file dat/formatvcd.txt, the program produces the following output:
$ ./bin/formatVCD dat/formatvcd.txt "VCD"
Ok1
oK2
What about the case-insensitive format string?
$ ./bin/formatVCD dat/formatvcd.txt "VcD"
Ok1
oK2
How about matching other formats?
$ ./bin/formatVCD dat/formatvcd.txt "DVC"
2og
or
$ ./bin/formatVCD dat/formatvcd.txt "VC"
Ok
What about the format string check?
$ ./bin/formatVCD dat/formatvcd.txt "VqD"
error: invalid char 'q' is not one of "VCD".
What about a short format string?
$ ./bin/formatVCD dat/formatvcd.txt "V"
(no output)
Always try and test all cases you can think of to fully test your code. Even doing that, there are usually creative folks here that can point out cases you haven't considered.
If you have trouble putting the code together, let me know and I'm happy to post a completed version. All you need to do is paste the two functions below main() in the complete version of main() above. If you are compiling with gcc, you can use:
$ gcc -Wall -Wextra -pedantic -Wshadow -Werror -Wno-implicit-fallthrough -std=c11 -Ofast -o formatVCD formatVCD.c
To compile the executable to formatVCD from the source formatVCD.c with full warnings enable and treating warnings as errors. All modern compilers will have equivalent option, just check their documentation.
footnotes:
man pages are a bit cryptic when you first look at them, but they are very well written to give you the precise declaration, syntax, usage and any header or definitions required. Take a few minutes and make friends with the man-page format and then use them to lookup each function you use.
#David Rankin has provided an OUTSTANDING answer to this OP. (Kudos Mr. Rankin!)
Because one should always strive for more, I'm posting my 'abbreviated' version of the algorithm. I didn't mess with files or command line arguments in order to focus on maximising the effectiveness of the code. Padding this out with validation (and perhaps some "data conditioning") would detract from its brevity.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main() {
char *legit[] = {
"",
"BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz",
"0123456789",
"AEIOUaeiou",
};
char *formato = "VCD";
char *arr[] = { "2og","Ok1","Ok","oK2", };
for( int i = 0; i < sizeof arr/sizeof arr[0]; i++ ) {
char *str = arr[ i ];
char *wnt = formato;
while( *wnt && *str && strchr( legit[ *wnt>>1&3 ], *str ) )
wnt++, str++;
if ( !*str && !*wnt )
puts( arr[ i ] );
}
return 0;
}
Output:
Ok1
oK2
With nothing better to do, here's another alternative that doesn't have the overhead of function calls for each character being checked. (This is a custom version of the facilities of some of the "ctype" functions.) This version relies on the "format specifier string" being all upper case.
#define V 'V'
#define C 'C'
#define D 'D'
#define B 1
char tbl[] = {
B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
D, D, D, D, D, D, D, D, D, D, B, B, B, B, B, B,
B, V, C, C, C, V, C, C, C, V, C, C, C, C, C, V,
C, C, C, C, C, V, C, C, C, C, C, B, B, B, B, B,
B, V, C, C, C, V, C, C, C, V, C, C, C, C, C, V,
C, C, C, C, C, V, C, C, C, C, C, B, B, B, B, B,
};
#undef B
#undef D
#undef C
#undef V
int main() {
char *formato = "VCD";
char *arr[] = { "2og", "Ok1", "Ok", "oK2", };
for( int i = 0; i < sizeof arr/sizeof arr[0]; i++ ) {
char *str = arr[ i ];
char *wnt = formato;
while( *wnt == tbl[ *str ] )
wnt++, str++;
if ( !*str && !*wnt )
puts( arr[ i ] );
}
return 0;
}
The following is an alternative (and slightly compact) initialisation of tbl[] for use with the same code shown above.
char tbl[] = { // 'B'ad, 'V'owel, 'C'onsonant, 'D'igit
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBDDDDDDDDDDBBBBBB"
// 0123456789
"BVCCCVCCCVCCCCCVCCCCCVCCCCCBBBBBBVCCCVCCCVCCCCCVCCCCCVCCCCCBBBBB"
// ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz
};

sscanf doesnt seem to capture the correct parts of my strings

I've tried to run this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char a[1000];
void eliminatesp() {
char buff1[1000], buff2[1000];
LOOP: sscanf(a,"%s %s",buff1,buff2);
sprintf(a,"%s%s", buff1, buff2);
for(int i=0; i<strlen(a); ++i) {
if(a[i]==' ') goto LOOP;
}
}
void eliminateline() {
char buff1[1000]; char buff2[1000];
LOOP: sscanf(a,"%s\n\n%s",buff1,buff2);
sprintf(a,"%s\n%s", buff1, buff2);
for(int i=0; i<strlen(a)-1; ++i) {
if(a[i]=='\n'&&a[i+1]=='\n') goto LOOP;
}
}
int main() {sprintf(a,"%s\n\n%s", "hello world","this is my program, cris");
eliminatesp();
eliminateline();
printf("%s",a); return 0;
return 0;
}
but the output was:
hello world
world
How can I correct it? I was trying to remove spaces and empty lines.
Going with your idea of using sscanf and sprintf you can actually eliminate both spaces and newlines in a single function, as sscanf will ignore all whitespace (including newlines) when reading the input stream. So something like this should work:
void eliminate() {
char buff1[1000], buff2[1000], b[1000];
char* p = a, *q = b, *pq = b;
sprintf(q, "%s", p);
while (q != NULL && *q != '\0')
{
if (iswspace(*q))
{
sscanf(pq, "%s %s", buff1, buff2);
sprintf(p, "%s%s", buff1, buff2);
p += strlen(buff1);
pq = ++q;
}
q++;
}
}
Pedro, while the %s format specifier does stop conversion on the first encountered whitespace, it isn't the only drawback to attempting to parse with sscanf. In order to use sscanf you will also need to use the %n conversion specifier (the number of characters consumed during conversion to the point the %n appears) and save the value as an integer (say offset). Your next conversion will begin a a + offset until you have exhausted all words in 'a'. This can be a tedious process.
A better approach can simply be to loop over all characters in 'a' copying non-whitespace and single-delimiting whitespace to the new buffer as you go. (I often find it easier to copy the full string to a new buffer (say 'b') and then read from 'b' writing the new compressed string back to 'a').
As you work your way down the original string, you use simple if else logic to determine whether to store the current (or last) character or whether to just skip it and get the next. There are many ways to do this, no one way more right than the other as long as they are reasonably close in efficiency. Making use of the <ctype.h> functions like isspace() makes things easier.
Also, in your code, avoid the use of global variables. There is no reason you can't declare 'a' in main() and pass it as a parameter to your eliminate functions. If you need a constant in your code, like 1000, then #define a constant and avoid sprinkling magic numbers throughout your code.
Below is an example putting all those pieces together, and combining both your eliminatesp and eliminateline functions into a single eliminatespline function that does both trim whitespace and eliminate blank lines. This will handle blank lines and considers lines containing only whitespace characters as blank.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAXL 1000 /* if you need a constant, define one (or more) */
/** trim leading, compress included, and trim trailing whitespace.
* given non-empty string 'a', trim all leading whitespace, remove
* multiple included spaces and empty lines, and trim all trailing
* whitespace.
*/
void eliminatespline (char *a)
{
char b[MAXL] = "", /* buffer to hold copy of a */
*rp = b, /* read pointer to copy of a */
*wp = a, /* write pointer for a */
last = 0; /* last char before current */
if (!a || !*a) /* a NULL or empty - return */
return;
strcpy (b, a); /* copy a to b */
while (isspace (*rp)) /* skip leading whitespace */
rp++;
last = *rp++; /* fill last with 1st non-whitespace */
while (*rp) { /* loop over remaining chars in b */
/* last '\n' and current whitespace - advance read pointer */
if (last == '\n' && isspace(*rp)) {
rp++;
continue;
} /* last not current or last not space */
else if (last != *rp || !isspace (last))
*wp++ = last; /* write last, advance write pointer */
last = *rp++; /* update last, advance read pointer */
}
if (!isspace (last)) /* if last not space */
*wp++ = last; /* write last, advance write pointer */
*wp = 0; /* nul-terminate at write pointer */
}
int main() {
char a[] = " hello world\n \n\nthis is my program, cris ";
eliminatespline (a);
printf ("'%s'\n", a);
return 0;
}
note: the line being trimmed has both leading and trailing whitespace as well as embedded blank lines and lines containing only whitespace, e.g.
" hello world\n \n\nthis is my program, cris "
Example Use/Output
$ ./bin/elimspaceline
'hello world
this is my program, cris'
(note: the printf statements wraps the output in single-quotes to confirm all leading and trailing whitespace was eliminated.)
If you did want to use sscanf, you could essentially do the same thing with sscanf (using the %n specifier to report characters consumed) and a array of two-characters to treat the next character as a string, and do something like the following:
void eliminatespline (char *a)
{
char b[MAXL] = "", /* string to hold build w/whitespace removed */
word[MAXL] = "", /* string for each word */
c[2] = ""; /* string made of char after word */
int n = 0, /* number of chars consumed by sscanf */
offset = 0; /* offset from beginning of a */
size_t len; /* length of final string in b */
/* sscanf each word and char that follows, reporting consumed */
while (sscanf (a + offset, "%s%c%n", word, &c[0], &n) == 2) {
strcat (b, word); /* concatenate word */
strcat (b, c); /* concatenate next char */
offset += n; /* update offset with n */
}
len = strlen (b); /* get final length of b */
if (len && isspace(b[len - 1])) /* if last char is whitespace */
b[len - 1] = 0; /* remove last char */
strcpy (a, b); /* copy b to a */
}
Look things over, try both approaches and let me know if you have further questions.

K&R, find longest line, same code but not working?

I've just started to read K&R and on pages 32-33, there is a code that
finds the longest line among the inputs.
I nearly completely copy-pasted the code given in the book, just added some comment lines to make the code more understandable for me. But it isn't working.
Edit: I'm sorry for bad questioning. It seems the program does not act properly when I press Ctrl + Z, in order to terminate it. No matter how many lines I type and how many times I press Ctrl + Z, it just does nothing.
The following is my version of the code:
/* Find the longest line among the giving inputs and print it */
#include <stdio.h>
#define MAXLINE 1000 /* maximum input line length */
int getLine(char line[], int maxLine);
void copy(char to[], char from[]);
int main(void) {
int len; /* current line length */
int max; /* maximum length seen so far */
char line[MAXLINE]; /* current input line */
char longest[MAXLINE]; /* longest line saved here*/
max = 0;
/* getLine function takes all the input from user, returns it's size and equates it to the variable len
* Then, len is compared whether it's greater than zero because if there's no input, no need to do any calculation
* EDGE CASE
*/
while ((len = getLine(line, MAXLINE)) > 0)
/* If the length of input is larger than the previous max length, set max as the new length value and copy that input */
if (len > max) {
max = len;
copy(longest, line);
}
if (max > 0) /* there was a line, EDGE CASE */
printf("%s", longest);
return 0;
}
/* Read a line into s, return length.
* Since the input length is unknown, there should be a limit */
int getLine(char s[], int lim) {
int c, i;
/* The loop's first condition is whether the input length is below the limit. EDGE CASE
* If it's not, omit the rest because it would cause a BUFFER OVERFLOW. Next, take the input as long as it's not an EOF command.
* Finally, if the input is end of line, finish the loop, don' take it.
*/
for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; i++)
s[i] = c;
if (c == '\n')
s[i++] = c;
s[i++] = '\0'; // always put a '\0' character to a string array ending, so that the compiler knows it's a string.
return i;
}
void copy(char to[], char from[]) {
int i = 0;
// This loop is readily assigns all chars from the source array to the target array until it reaches the ending char.
while ((to[i] = from[i]) != '\0')
++i;
}
Thanks in advance!
Okay, here's the error:
s[i++] = '\0'; // always put a '\0' character to a string array ending, so that the compiler knows it's a string.
This will cause it to terminate the string even for no input (when it got EOF directly), and since it increments i before returning it, getLine() will never return 0 and thus main() will never stop. Removing the ++ fixed it, for my simple test.
Also, the comment is misleading, the compiler doesn't know anything. The compiler is no longer around when the code runs; the in-memory format of strings is something that's needed to keep the run-time libraries happy since that's what they expect.

Rotate words around vowels in C

I am trying to write a program that reads the stdin stream looking for words (consecutive alphabetic characters) and for each word rotates it left to the first vowel (e.g. "friend" rotates to "iendfr") and writes this sequence out in place of the original word. All other characters are written to stdout unchanged.
So far, I have managed to reverse the letters, but have been unable to do much more. Any suggestions?
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define MAX_STK_SIZE 256
char stk[MAX_STK_SIZE];
int tos = 0; // next available place to put char
void push(int c) {
if (tos >= MAX_STK_SIZE) return;
stk[tos++] = c;
}
void putStk() {
while (tos >= 0) {
putchar(stk[--tos]);
}
}
int main (int charc, char * argv[]) {
int c;
do {
c = getchar();
if (isalpha(c) && (c == 'a' || c == 'A' || c == 'e' || c == 'E' || c == 'i' || c == 'o' || c == 'O' || c == 'u' || c == 'U')) {
push(c);
} else if (isalpha(c)) {
push(c);
} else {
putStk();
putchar(c);
}
} while (c != EOF);
}
-Soul
I am not going to write the whole program for you, but this example shows how to rotate a word from the first vowel (if any). The function strcspn returns the index of the first character matching any in the set passed, or the length of the string if no matches are found.
#include <stdio.h>
#include <string.h>
void vowelword(const char *word)
{
size_t len = strlen(word);
size_t index = strcspn(word, "aeiou");
size_t i;
for(i = 0; i < len; i++) {
printf("%c", word[(index + i) % len]);
}
printf("\n");
}
int main(void)
{
vowelword("friend");
vowelword("vwxyz");
vowelword("aeiou");
return 0;
}
Program output:
iendfr
vwxyz
aeiou
There are a number of ways your can approach the problem. You can use a stack, but that just adds handling the additional stack operations. You can use a mathematical reindexing, or you can use a copy and fill solution where you copy from the first vowel to a new string and then simply add the initial characters to the end of the string.
While you can read/write a character at a time, you are probably better served by creating the rotated string in a buffer to allow use of the string within your code. Regardless which method you use, you need to validate all string operations to prevent reading/writing beyond the end of your input and/or rotated strings. An example of a copy/fill approach to rotating to the first vowel in your input could be something like the following:
/* rotate 's' from first vowel with results to 'rs'.
* if 's' contains a vowel, 'rs' contains the rotated string,
* otherwise, 'rs' contais 's'. a pointer to 'rs' is returned
* on success, NULL otherwise and 'rs' is an empty-string.
*/
char *rot2vowel (char *rs, const char *s, size_t max)
{
if (!rs || !s || !max) /* validate params */
return NULL;
char *p = strpbrk (s, "aeiou");
size_t i, idx, len = strlen (s);
if (len > max - 1) { /* validate length */
fprintf (stderr, "error: insuffieient storage (len > max - 1).\n");
return NULL;
}
if (!p) { /* if no vowel, copy s to rs, return rs */
strcpy (rs, s);
return rs;
}
idx = p - s; /* set index offset */
strcpy (rs, p); /* copy from 1st vowel */
for (i = 0; i < idx; i++) /* rotate beginning to end */
rs[i+len-idx] = s[i];
rs[len] = 0; /* nul-terminate */
return rs;
}
Above, strpbrk is used to return a pointer to the first occurrence of a vowel in string 's'. The function takes as parameters a pointer to a adequately sized string to hold the rotated string 'rs', the input string 's' and the allocated size of 'rs' in 'max'. The parameters are validated and s is checked for a vowel with strpbrk which returns a pointer to the first vowel in s (if it exists), NULL otherwise. The length is checked against max to insure adequate storage.
If no vowels are present, s is copied to rs and a pointer to rs returned, otherwise the pointer difference is used to set the offset index to the first vowel, the segment of the string from the first vowel-to-end is copied to rs and then the preceding characters are copied to the end of rs with the loop. rs is nul-terminated and a pointer is returned.
While I rarely recommend the use of scanf for input, (a fgets followed by sscanf or strtok is preferable), for purposes of a short example, it can be used to read individual strings from stdin. Note: responding to upper/lower case vowels is left to you. A short example setting the max word size to 32-chars (31-chars + the nul-terminating char) will work for all known words in the unabridged dictionary (longest word is 28-chars):
#include <stdio.h>
#include <string.h>
enum { BUFSZ = 32 };
char *rot2vowel (char *rs, const char *s, size_t max);
int main (void)
{
char str[BUFSZ] = {0};
char rstr[BUFSZ] = {0};
while (scanf ("%s", str) == 1)
printf (" %-8s => %s\n", str, rot2vowel (rstr, str, sizeof rstr));
return 0;
}
Example Use/Output
(shamelessly borrowing the example strings from WeatherVane :)
$ echo "friend vwxyz aeiou" | ./bin/str_rot2vowel
friend => iendfr
vwxyz => vwxyz
aeiou => aeiou
Look it over and let me know if you have any questions. Note: you can call the rot2vowel function prior to the printf statement and print the results with rstr, but since the function returns a pointer to the string, it can be used directly in the printf statement. How you use it is up to you.

How to do a split of a string

HI, I would like how to do a split of a string in c without #include
Multiple ways of doing that, which I'll just explain and not write for you as this can only be a homework (or self-enhancement exercise, so the intent is the same).
Either you split the string into multiple strings that you re-allocate into a multi-dimensional array,
or you simply cut the string on separators and add terminal '\0' where appropriate and just copy the starting address of each sub-string to an array of pointers.
The approach for the splitting is similar in both cases, but in the second one you don't need to allocate any memory (but modify the original string), while in the first one you create safe copies of each sub-string.
You were not specific on the splitting, so I don't know if you wanted to cut on substrings, a single charater, or a list of potential separators, etc...
Good luck.
find the point you would like to split it
make two buffers large enough to contain data
strcpy() or do it manually (see example)
in this code I assume you have a string str[] and would like to split it at the first comma:
for(int count = 0; str[count] != '\0'; count++) {
if(str[count] == ',')
break;
}
if(str[count] == '\0')
return 0;
char *s1 = malloc(count);
strcpy(s1, (str+count+1)); // get part after
char *s2 = malloc(strlen(str) - count); // get part before
for(int count1 = 0; count1 < count; count1++)
s2[count1] = str[count1];
got it? ;)
Assuming I have complete control of the function prototype, I'd do this (make this a single source file (no #includes) and compile, then link with the rest of the project)
If #include <stddef.h> is part of the "without #include" thing (but it shouldn't), then instead of size_t, use unsigned long in the code below
#include <stddef.h>
/* split of a string in c without #include */
/*
** `predst` destination for the prefix (before the split character)
** `postdst` destination for the postfix (after the split character)
** `src` original string to be splitted
** `ch` the character to split at
** returns the length of `predst`
**
** it is UB if
** src does not contain ch
** predst or postdst has no space for the result
*/
size_t split(char *predst, char *postdst, const char *src, char ch) {
size_t retval = 0;
while (*src != ch) {
*predst++ = *src++;
retval++;
}
*predst = 0;
src++; /* skip over ch */
while ((*postdst++ = *src++) != 0) /* void */;
return retval;
}
Example usage
char a[10], b[42];
size_t n;
n = split(b, a, "forty two", ' ');
/* n is 5; b has "forty"; a has "two" */

Resources