I was trying to solve a programming problem of some site and this one had the following statement:
Read a string and parse it as a number, char 'l' can be considered as number 1 and chars 'o' and 'O' can be considered as number 0, commas and spaces will be accepted in the input but ignored, if any other character is found then output error...
So... since there can be spaces in the lines, I used gets (the documentation says it removes the new line and puts a terminator)...
My sequence of IF test if it is a number, then if its an acceptable letter, then checks if it is not a comma or a space... And I found out that it was almost always entering in the last IF even though there wasn't any character that should lead it there so I changed the printf inside it to print the
printf("%d error", st[k]);
And it outputs 13: carriage return... I tried this compiler here
#include <cstdio>
#include <cstring>
#include <cstdlib>
int main()
{
char st[100];
char buf[100];
int k, i;
long long int num;
ops: while(gets(st))
{
for(k = i = 0; st[k]; k++)
if(st[k] >= '0' && st[k] <= '9')
buf[i++] = st[k];
else if(st[k] == 'o' || st[k] == 'O')
buf[i++] = '0';
else if(st[k] == 'l')
buf[i++] = '1';
else if(st[k] != ',' && st[k] != ' ')
{
printf("error\n");
goto ops;
}
// remaining code comes here...
}
The input sample had the following lilnes:
lo6
234,657
hi
,,,,,5,,5, 4
2200000000
00
Should I use other function to read instead?
Any suggestions on how to avoid this damn Carriage Return?
The statemente for the problem can be seen here if you want more detail
Thanks
EDIT:
I'm asking that because there seem to be a difference between the compiler I'm using and the compiler the website was using, once I submitted a code that wasn't generating the correct output on mine but I thought the code was correct... and it passed. Then after it, I tried the code on a linux virtual machine and also correct but my gcc on windows failed... some characters were completely away from where they should be
The thing is:
The preferred method of line input is getline. If you do not know the width of the input beforehand, getline will allocate space for you if you set the buffer pointer to NULL.
Since you indicate you have some experience in C, the following will show you how to step through an input string and parse it in the manner you want. While there are many ways to handle parsing strings, it is hard to beat assigning a pointer to the beginning of the string and then advancing down the string until you reach the null-terminating character (unsigned 0, or char '\0'). If you have any questions after looking it over, just ask:
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[]) {
char *buffer = NULL;
size_t n = 0;
ssize_t len;
char *ptr = NULL;
char *output = NULL;
int idx = 0;
printf ("\nEnter your string below: ([ctrl + d] on blank line to end)\n");
while ((len = getline (&buffer, &n, stdin)) != -1) {
ptr = buffer;
idx = 0;
output = malloc (sizeof (char) * len);
while (*ptr != 0) {
if (*ptr >= 48 && *ptr <= 57) output [idx++] = *ptr;
if (*ptr == 'I') output [idx++] = '1';
if (*ptr == 'o' || *ptr == 'O') output [idx++] = '0';
ptr++;
}
output [idx] = 0;
printf ("\n valid output: %s\n\n", output);
free (output);
}
return 0;
}
output:
Enter your string below: ([ctrl + d] on blank line to end)
This sting has 12345 aeiou AEIOU,,,,,commas, and 99, to end.
valid output: 123450100990
Related
I want to replace a pair of characters with another pair.
For example if want to replace "ax" with "57" and the string is "vksax", it will give out "vks57".
Tried it this way but get a weird output that is only partially correct:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char dummy;
int i;
char string[16];
scanf("%s", string);
for(i=0;string[i];i++){
if(string[i] == 'a' && string[i+1] == 'x'){
string[i] = '5' ;
string[i+1] = '7';
i=0;
}
printf("%s", string) ;
}
exit(0);
}
I can't figure out why it gives, for example, with an input of 1234ax the output 1234ax1234ax1234ax1234ax123457123457123457123457123457123457
Within the for loop
for(i=0;string[i];i++){
if(string[i] == 'a' && string[i+1] == 'x'){
string[i] = '5' ;
string[i+1] = '7';
i=0;
}
this statement
i=0;
does not make a sense. Remove it. And instead of
string[i+1] = '7';
write
string[++i] = '7';
And move this call
printf("%s", string) ;
outside the for loop.
Pay attention to that the variable dummy is declared but not used.
char dummy;
Remove this declaration.
And there is no great sense to use exit( 0 ); instead of return 0;.
And the call of scanf will be more safer if to write it like
scanf("%15s", string);
Since this question is marked as C++, here is C++ one-line answer:
#include <string>
std::string String = "vksax";
String.replace(String.find("ax"), 2, "57");
Get yourself familiar with std::string
https://www.cplusplus.com/reference/string/string/
Solution as function(it assumes substring to be replaced and replacement have the same length):
std::string FindAndReplace(std::string sInputString, std::string sReplaceThis, std::string sReplacement)
{
size_t iPos = sInputString.find(sReplaceThis);
if(iPos == std::string::npos)
return sInputString;
sInputString.replace(iPos, sReplacement.length(), sReplacement);
return sInputString;
};
I attempted writing a thesaurus program which reads a thesaurus file, for example:
drink:beverage
clever:smart,witty
and a .txt document, changing up the words it finds from the thesaurus and creating a new document with the modified text. However there appears to be a bug, I have narrowed it down to the while loop in getReplacement(), by checking a print operation before and after. I would really appreciate someone finding why it won't work.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <iostream>
char* getReplacement(char* original, FILE* file);
int main(int argc, char* argv[])
{
using namespace std;
FILE* thes = fopen(argv[1], "r");
FILE* text = fopen(argv[2], "r+");
FILE* nText = fopen("temp.txt", "w");
if(thes == NULL || text == NULL || nText == NULL)
return 1;
char word[20] = {};
char c;
int bytesW=0;
while((c = fgetc(text)) != EOF)
{
fputc(c, nText);
bytesW++;
if(isalpha(c))
{
int len = strlen(word);
word[len] = c;
word[len + 1] = '\0';
}
else
{
if(word == "")
continue;
cout << 7<<endl;
char* replacement = getReplacement(word, thes);
if(replacement == NULL)
continue;
fseek(nText,bytesW-1-strlen(word),SEEK_SET);
for(int i=0;i<strlen(replacement);i++)
fputc(replacement[i],nText);
int diff = strlen(word) - strlen(replacement);
while(diff-- >0)
fputc(' ', nText);
bytesW = bytesW-1-strlen(word)+strlen(replacement);
fseek(nText, bytesW, SEEK_SET);
}
}
fclose(thes);
fclose(text);
fclose(nText);
return 0;
}
char* getReplacement(char* const original, FILE* file)
{
using namespace std;
char* line="";
const short len = strlen(original);
int numOfOptions=1;
int toSkip=0; // number of commas to skip over
outer: while(fgets(line,1000,file) != NULL)
{
for(int i=0;i<len;i++)
if(line[i] != original[i])
{
goto outer;
}
if(line[len] != ':')
goto outer;
for(int i=0;i<len;i++)
line++;
for(int i=0;i<strlen(line);i++)
if(line[i] == ',')
numOfOptions++;
toSkip = rand()%numOfOptions;
while(toSkip >0)
{
if(line[0] == ',')
toSkip--;
line++;
}
return line;
}
return NULL;
}
char* line="";
// ... snip ...
outer: while(fgets(line,1000,file) != NULL)
Here's your problem. You are trying to read into a literal string; you instead need to allocate an array, on the stack or via malloc() to read into.
A string that you write in quotes in C is known as a literal. This means that this string gets embedded in the code of your program, and later loaded into memory when your programs is loaded. Usually it gets loaded into memory that's marked read-only, but that's platform dependent. That string that you wrote has room only for the null terminator. But you are trying to read up to 1000 characters into it. This will either lead to a segmentation fault because you were writing to read-only memory, or will lead to you writing all over some other memory, producing who knows what behavior.
What you want to do instead is allocate a buffer that you can read into:
char line[1000];
or, if you have limited stack space:
char *line = malloc(1000 * sizeof(char));
Furthermore, in your main() function, you do:
char c;
while((c = fgetc(text)) != EOF)
fgetc() returns an int, not a char. This way, it can return a value corresponding to a valid character if a value is read, or a value that is outside that range if you hit the end of file.
You can't compare strings in C using ==; what that does is compare whether they are the same pointer, not whether they have the same contents. It doesn't really make sense to recalculate the length of the current word each time; why not just keep track of len yourself, incrementing it every time you add a character, and then when you want to check if the word is empty, check if len == 0? Remember to reset len to 0 after the end of the word so you'll start over on the next word. Also remember to reset if len goes over sizeof(word); you don't want to write more than word can hold, or you will start scribbling all over random stuff on your stack and lots of things will break.
I'm looking to make a method like so (which encrypts a message using Caesar Cipher, entered by the user and displays it):
void encrypt(char *message, int shift);
My code:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <string.h>
char num(char c)
{
const char upper_alph[26] = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
const char lower_alph[26] = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
if(isupper(c)) {
for(int i = 0; i < 26; i++)
if(upper_alph[i] == c)
return i;
} else {
for(int i = 0; i < 26; i++)
if(lower_alph[i] == c)
return i;
}
return 0;
}
void encrypt(char *message, int shift)
{
int i = 0;
const char upper_alph[26] = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
const char lower_alph[26] = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
while(message[i] != NULL)
{
if(isalpha(message[i]))
{
if(isupper(message[i])) {
printf("%c", upper_alph[(num(message[i])+shift)%26]);
} else {
printf("%c", lower_alph[(num(message[i])+shift)%26]);
}
} else {
printf("%c", message[i]);
}
i++;
}
}
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
int main()
{
//reverse();
//printf("\n\n");
int rc;
char mes[1024];
int sh = 0;
rc = getLine ("Enter message to be encrypted: ", mes, sizeof(mes));
if (rc == NO_INPUT) {
// Extra NL since my system doesn't output that on EOF.
printf ("\nNo input\n");
return 1;
}
if (rc == TOO_LONG) {
printf ("Input too long [%s]\n", mes);
return 1;
}
encrypt(mes, 1);
fflush(stdin);
getchar();
return 0;
}
Thank you to anyone who helps or tries to help.
:)
EDIT: Made many corrections. Still not working :/
EDIT2: Made a lot more corrections. Getting an access violation # "while(*message != '\0')"
EDIT3: Updated the code above to the working code. Thank you everyone for your help!
One problem is you never wrap-around. Consider if you are passed something like 'Z' or 'z' with any positive shift, then you will just increment outside of the array.
You need to do something like:
upper_alph[(num(message[i])+shift)%26]
and
lower_alph[(num(message[i])+shift)%26]
You also need to allocate memory for mes:
char mes[1024];
I believe your scanf is also incorrect (c is a character, s is a string):
scanf("%s", mes);
Using %s will however only read until it gets white-space, a better option may be to read the entire line with getline().
You'll get an "index out of bounds" error on these lines:
if(isupper(message[i])) {
printf("%c", upper_alph[num(message[i])+shift]);
} else {
printf("%c", lower_alph[num(message[i])+shift]);
}
You need to calculate the index in advance and make sure it is between 0 and 25:
int shiftedIndex = (num(message[i]) + shift) % 26;
You are aware of the fact that your code only works with English as input language?
It doesn't work because you didn't allocate memory for mes:
char mes[512]; // Enough space!
Use std::string is easier:
string mes;
int sh = 0;
cout << "Enter message to be encrypted: " << endl;
getline(cin, mes);
cout << "Enter a shift amount (1-25): " << endl;
cin >> sh;
encrypt(mes, sh);
And change encrypt function to:
void encrypt(const string &message, int shift)
And keep your characters in range:
upper_alph[(num(message[i])+shift)%26]
lower_alph[(num(message[i])+shift)%26]
There is a fundamental problem here that the OP isn't understanding. And that is, to the computer, letters are just numbers. It us us humans that assign meaning to those numbers, and we can't even decide on which numbers mean what (see comments on question re ASCII, EBDIC and Unicode).
Here is a table showing how the ASCII standard maps the numbers to letters.
Notice that the character 'a' is 97, 'b' is 98, 'c' is 99 and so on. The uppercase characters start at 65 and go up from there. Note also that the letter 'a' and 'A' are on the same row! This means the bit patterns of the lower 5 bits for an upper case letter and a lower case letter are the same. Finally, as the computer only ever sees characters as numbers, it can do numeric operations on them:-
'd' - 'a' == 3
100 - 97
The second thing to note is that mathematically the Caeser cipher is just an addition with a modulo:-
encoded character = (plain text character + shift) mod 26
So now the code can written much more efficiently:-
void Encode (char *message, int shift)
{
while (*message)
{
char c = *message;
if (isalpha (c)) // check c is a letter
{
// get the letter index: this maps 'A' to 0, 'B' to 1, etc
// it also maps 'a' to 32 (97 - 65), 'b' to 33, etc
c -= 'A';
// this is 32 for lower case characters and 0 for upper case
char case_of_c = c & 32;
// map 'a' to 'A', 'b' to 'B'
c &= 31;
// the caeser shift!
c = (c + shift) % 26;
// restore the case of the letter
c |= case_of_c;
// remap the character back into the ASCII value
c += 'A';
// save the result of the shift
*message = c;
}
++message;
}
}
Here is a problem from spoj. nothing related to algorithms, but just c
Sample Input
2
a aa bb cc def ghi
a a a a a bb bb bb bb c c
Sample Output
3
5
it counts the longest sequence of same words
http://www.spoj.pl/problems/WORDCNT/
The word is less than 20 characters
But when i run it, it's giving segmentation fault. I debugged it using eclipse. Here's where it crashes
if (strcmp(previous, current) == 0)
currentLength++;
with the following message
No source available for "strcmp() at 0x2d0100"
What's the problem?
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
using namespace std;
int main(int argc, const char *argv[])
{
int t;
cin >> t;
while (t--) {
char line[20000], previous[21], current[21], *p;
int currentLength = 1, maxLength = 1;
if (cin.peek() == '\n') cin.get();
cin.getline(line, 20000);
p = strtok(line, " '\t''\r'");
strcpy(previous, p);
while (p != NULL) {
p = strtok(NULL, " '\t''\r'");
strcpy(current, p);
if (strcmp(previous, current) == 0)
currentLength++;
else
currentLength = 1;
if (currentLength > maxLength)
maxLength = currentLength;
}
cout << maxLength << endl;
}
return 0;
}
The problem is probably here:
while (p != NULL) {
p = strtok(NULL, " '\t''\r'");
strcpy(current, p);
While p may not be NULL when the loop is entered.
It may be NULL when strcpy is used on it.
A more correct form of the loop would be:
while ((p != NULL) && ((p = strtok(NULL, " \t\r")) != NULL))
{
strcpy(current, p);
Note. Tokenizing a stream in C++ is a lot easier.
std::string token;
std::cin >> token; // Reads 1 white space seoporated word
If you want to tokenize a line
// Step 1: read a single line in a safe way.
std::string line;
std::getline(std::cin, line);
// Turn that line into a stream.
std::stringstream linestream(line);
// Get 1 word at a time from the stream.
std::string token;
while(linestream >> token)
{
// Do STUFF
}
Forgot to check for NULL on strtok, it will return NULL when done and you cannot use that NULL on strcpy, strcmp, etc.
Note that you do a strcpy right after the strtok, you should check for null before doing that using p as a source.
The strtok man page says:
Each call to strtok() returns a pointer to a null-terminated string containing the next
token. This string does not include the delimiting character. If no more tokens are found,
strtok() returns NULL.
And in your code,
while (p != NULL) {
p = strtok(NULL, " '\t''\r'");
strcpy(current, p);
you are not checking for NULL (for p) once the whole string has been parsed. After that you are trying to copy p (which is NULL now) in current and so getting the crash.
You will find that one of previous or current does not point to a null-terminated string at that point, so strcmp doesn't know when to stop.
Use proper C++ strings and string functions instead, rather than mixing C and C++. The Boost libraries can provide a much safer tokeniser than strtok.
You probably undersized current and previous. You should really use std::string for this kind of thing- that's what it's for.
You are doing nothing to check your string lengths before copying them into buffers of size 21. I bet that you are somehow overwriting the end of the buffer.
If you insist on using C strings, I'd suggest using strncmp instead of strcmp. That way, in case you are ending up with a non-null terminated string (which is what I suspect is the case), you can restrict your compare to the max length of the string (in this case 21).
Try this one...
#include <cstdio>
#include <cstring>
#define T(x) strtok(x, " \n\r\t")
char line[44444];
int main( )
{
int t; scanf("%d\n", &t);
while(t--)
{
fgets(line, 44444, stdin);
int cnt = 1, len, maxcnt = 0, plen = -1;
for(char *p = T(line); p != NULL; p = T(NULL))
{
len = strlen(p);
if(len == plen) ++cnt;
else cnt = 1;
if(cnt > maxcnt)
maxcnt = cnt;
plen = len;
}
printf("%d\n", maxcnt);
}
return 0;
}
I have this simple problem that gets an input from the user using a function then checks if the input is 'equal' to the "password". However, strcmp would never return my desired value, and the culprit is somewhere in my loop that uses getch() to take each character separately and add them to the character array. I found this out by having printf display the character array. If I type in pass word, the function would display it as pass word ". I have no idea on why the closing double quote and a whitespace was included in the array right after the word I typed in. Any idea? Here's the code. Thanks.
#include <stdio.h>
#include <iostream>
#include <conio.h>
#include <string.h>
int validateUser();
int main()
{
for(int x = 0;x<2;x++)
{
if(validateUser())
{
system("cls");
printf("\n\n\t\t** Welcome **"); break;
}
else
{
system("cls");
printf("\n\n\t\tIntruder Alert!");
system("cls");
}
}
system("PAUSE>nul");
return 0;
}
int validateUser()
{
char password[9];
char validate[] = "pass word";
int ctr = 0, c;
printf("Enter password : ");
do
{
c = getch();
if(c == 32)
{
printf(" ");
password[ctr] = c;
}
if(c != 13 && c != 8 && c != 32 )
{
printf("*");
password[ctr] = c;
}
c++;
}while(c != 13);
return (!strcmp(password, validate));
}
Your char array password does not
have a terminating null char.
You need to ensure that you don't
stuff more than 8 char into
password
Also c++ should be ctr++
.
do {
// stuff char into password.
ctr++;
}while(c != 13 && ctr <8);
password[ctr] = 0;
You're incrementing c in your loop. You should be incrementing ctr. Also, all the stuff everyone else has said (null terminator, only 8 characters, etc).
getch() is a function defined in a non-standard header <conio.h>. Relying on non-standard features is not recommended when you want your code to be portable. :)
do {
// stuff char into password.
++ctr;
} while(c != 13 && ctr < 9);
password[ctr] = '\0';