I am working on ROT13 for c++ practice. however this bit of code here returns an error and fails to compile, i do not understand why! I am posting a snippet of code in the following lines
string encode(string &x)
{
char alphabet[] = "abcdefghijklmnopqrstuvwxyz";
for (size_t l=0;l<x.size();++l){
cout<<x[l];
cout<< strchr(alphabet,x[l]);
}
return x;
}
Q2. Also help me return the index of the matching letter from alphabet[] (e.g.,5 for 'f') to which i can add 13 and append that to x and so on ..
Q3. Besides practice, which course in CS would help me develop more efficient algorithms? Is it theory of computation, discrete mathematics, or algorithms ?
In order, starting with question 1:
The following compiles fine for me:
#include <iostream>
#include <cstring>
std::string encode(std::string &x)
{
char alphabet[] = "abcdefghijklmnopqrstuvwxyz";
char *ptr;
for (size_t l=0;l<x.size();++l){
std::cout<<x[l];
std::cout<< std::strchr(alphabet,x[l]);
}
return x;
}
int main (int argc, char* argv []) {
return 0;
}
Make sure:
you include the headers given, for cout and strchr.
use std:: prefixes unless you're using the std namespace.
fix that ptr problem.
Question 2:
If you're looking for a handy ROT-13 method, consider using two C strings, one for the source and one for the translation:
char from[] = "abcdefghijklmnopqrstuvwxyz";
char to [] = "nopqrstuvwxyzabcdefghijklm";
Then you can use strchr to look it up in the first one and use that pointer to find the equivalent in the second.
char src = 'j';
char *p = strchr (from, src);
if (p == NULL)
std::cout << src;
else
std::cout << to[p - from];
That would output the character as-is if it wasn't found or look up the translation if it was found. You may also want to put the capital letters in there as well.
Question 3:
If you want to learn about efficient algorithms, I'd go for, surprisingly enough, an algorithms course :-)
Theory of computation sounds a little dry, though it may well cover the theoretical basis behind algorithms. Discrete mathematics has applicability to algorithms but, again, it's probably very theoretical. That's all based on what the words mean, of course, the actual subject areas covered may be totally different, so you should probably take it up with the people offering the courses.
Extra bit:
If you're looking for something to compare your own work to, here's one I put together based on my suggestions above:
#include <iostream>
#include <cstring>
std::string rot13 (std::string x)
{
char from[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
char to [] = "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM";
std::string retstr = "";
for (size_t i = 0; i < x.size(); ++i) {
char *p = std::strchr (from, x[i]);
if (p == 0)
retstr += x[i];
else
retstr += to[p - from];
}
return retstr;
}
int main (int argc, char* argv []) {
std::string one = "This string contains 47 and 53.";
std::string two = rot13 (one);
std::string three = rot13 (two);
std::cout << one << '\n';
std::cout << two << '\n';
std::cout << three << '\n';
return 0;
}
The building of the return string could have probably been done more efficiently (such as a new'ed character array which becomes a string only at the end) but it illustrates the "lookup" part of the method well.
The output is:
This string contains 47 and 53.
Guvf fgevat pbagnvaf 47 naq 53.
This string contains 47 and 53.
which you can verify here, if necessary.
Cast alphabet to a const char*, it should work afterwards. Keep in mind that type[] is different from type *.
Related
I read this sample code in a book. I can't figure out why this part of the following sample code's function declaration is necessary:
while (i <= n)
p[i++] = '\0'; // set rest of string to '\0'
Here is the whole code:
#include <iostream>
const int ArSize = 80;
char * left(const char * str, int n = 1);
int main()
{
using namespace std;
char sample[ArSize];
cout << "Enter a string:\n";
cin.get(sample,ArSize);
char *ps = left(sample, 4);
cout << ps << endl;
delete [] ps; // free old string
ps = left(sample);
cout << ps << endl;
delete [] ps; // free new string
return 0;
}
// This function returns a pointer to a new string
// consisting of the first n characters in the str string.
char * left(const char * str, int n)
{
if(n < 0)
n = 0;
char * p = new char[n+1];
int i;
for (i = 0; i < n && str[i]; i++)
p[i] = str[i]; // copy characters
while (i <= n)
p[i++] = '\0'; // set rest of string to '\0'
return p;
}
I ran the code after I erased it and there was no problem.
The loop is unnecessary. Null-terminated strings end at the first null byte. If more memory was allocated than the actual string needs, it does not matter what’s in those extra bytes. All non-broken C-string handling code stops at the first null terminator. All that’s required is a single
p[i] = '\0';
after the for loop. However, that one null byte is mandatory. C-string functions depend on it and will happily overrun the allocated memory if it’s missing. Essentially they’ll (try to) keep going until they stumble upon the next null byte in memory. If that is past the allocated memory it causes undefined behaviour, resulting in a crash if you’re lucky; or corrupted data if you’re less lucky.
That said: Throw away that book yesterday. The code is a catastrophe from the first to the last line. It barely qualifies as C++. Most of it is plain C. And even as C code it’s highly questionable.
Why to avoid using namespace std. #vol7ron pointed out in the comments that the major complaint is against using namespace std in headers. Here it’s used inside a function in a .cpp file, which lessens the impact significantly. Although in my opinion it is still worth avoiding. If you don’t know the implementation of your standard library in depth, you don’t really have an idea about all the symbols you pull into your scope. If you need it for readability, pulling in specific symbols (e.g. using std::cout;) is a better choice. Also, I’m confident I’m not alone in kind of expecting the std:: prefix. For example, std::string is what I expect to see. string looks slightly off. There’s always a lingering doubt that it might not be the std library string, but a custom string type. So, including the prefix can benefit readability as well.
Why all the C-string pain? We’ve had std::string for a while now …
Copying characters in a loop? Seriously? That’s what std::strcpy() is for.
Raw new and delete everywhere: error prone because you have to keep track of the new/delete pairs manually to avoid memory leaks.
Even worse: asymmetric owning raw pointers. left() allocates and returns a pointer; and it’s the caller’s responsibility to delete it. It doesn’t get more error prone than that.
… And these are only the problems that stick out on first glance.
What that piece of code should look like:
#include <iostream>
#include <string>
std::string left(const std::string& str, std::size_t len = 1);
int main()
{
// getline can fail. If that happens we get an empty string.
std::string sample;
std::getline(std::cin, sample);
auto ps = left(sample, 4);
std::cout << ps << '\n';
ps = left(sample);
std::cout << ps << '\n';
return 0;
}
// `len` may be longer than the string. In that case a copy
// of the complete input string is returned.
std::string left(const std::string& str, std::size_t len)
{
return str.substr(0, len);
}
char* szWords[] = { "caralho", "porra" };
if (IsGoldGrade(pObj)) //Ignore, its ok. //Crashing after i added the
{
for (int x = 0; x < sizeof(szWords); x++) {
if (strstr((strlwr((char*)szWords[x])), szChat)) {
char szBuffer[256];
sprintf(szBuffer, "You can't type %s", szWords[x]);
Announce(pObj, szBuffer);
memset(szBuffer, 0, 256);
return;
}
}
}
Idk but I can't use this as "code" on stackoverflow.
Pastebin: http://pastebin.com/u8yit8Rw
PS: I can't use StrStrI because im using Visual Studio 2003.
Your for loop condition is wrong. You want to iterate the array of pointers to char.
Your loop for (int x = 0; x < sizeof(szWords); x++) continues while x < sizeof(szWords). But sizeof(szWords) is not array length. It just says how many bytes your array occupies in memory. It is system dependant, however it is twice the size of pointer to char, so probably 8 or 16 bytes. You need to divide this size by size of the array element then you will get the proper array size.
Rewrite your for loop like this:
for (int x = 0; x < sizeof(szWords)/sizeof(szWords[0]); x++)
or if your compiler supports C++11 you can try range-based for:
for (const char *word : szWords)
Apart from that, if you are writing C++ code you really should use STL and other C++ features. For instance your array of strings should be declared as:
std::vector<std::string> words = { "caralho", "porra" };
or if your compiler doesnt support C++11 (then really change it...)
std::vector<std::string> words;
words.push_back("caralho");
words.push_back("porra");
for (std::size_t i = 0; i < words.size(); ++i) {
// since you are using C string functions you will need word as C string
const char *word = words[i].c_str();
// do whatever you want with word
}
Also consider reading modern C++ book before writing code.
From the look of it, this is a function which checks if the user has written a prohibited word?
I'd replace char* szWords[]... with std::vector<std::string> to store the prohibited words, and use std::find to see if the input is in that list.
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
std::vector<std::string> bannedWords{"hamster", "elderberries", "etcetera"};
bool isBanned(const std::string &str) {
return std::find(bannedWords.begin(), bannedWords.end(), str) != bannedWords.end();
}
int main() {
std::cout << "Is 'wally' banned? " << isBanned("wally") << std::endl;
std::cout << "Is 'elderberries' banned? " << isBanned("elderberries") << std::endl;
}
More information about std::find is here.
Here's an online demo
This question already has answers here:
C++ Remove punctuation from String
(12 answers)
Closed 9 years ago.
In my program, I am checking whole cstring, if any spaces or punctuation marks are found, just add empty character to that location but the complilor is giving me an error: empty character constant.
Please help me out, in my loop i am checking like this
if(ispunct(str1[start])) {
str1[start]=''; // << empty character constant.
}
if(isspace(str1[start])) {
str1[start]=''; // << empty character constant.
}
This is where my errors are please correct me.
for eg the word is str,, ing, output should be string.
There is no such thing as an empty character.
If you mean a space then change '' to ' ' (with a space in it).
If you mean NUL then change it to '\0'.
Edit: the answer is no longer relevant now that the OP has edited the question. Leaving up for posterity's sake.
If you're wanting to add a null character, use '\0'. If you're wanting to use a different character, using the appropriate character for that. You can't assign it nothing. That's meaningless. That's like saying
int myHexInt = 0x;
or
long long myIndeger = L;
The compiler will error. Put in the value you wanted. In the char case, that's a value from 0 to 255.
UPDATE:
From the edit to OP's question, it's apparent that he/she wanted to trim a string of punctuation and space characters.
As detailed in the flagged possible duplicate, one way is to use remove_copy_if:
string test = "THisisa test;;';';';";
string temp, finalresult;
remove_copy_if(test.begin(), test.end(), std::back_inserter(temp), ptr_fun<int, int>(&ispunct));
remove_copy_if(temp.begin(), temp.end(), std::back_inserter(finalresult), ptr_fun<int, int>(&isspace));
ORIGINAL
Examining your question, replacing spaces with spaces is redundant, so you really need to figure out how to replace punctuation characters with spaces. You can do so using a comparison function (by wrapping std::ispunct) in tandem with std::replace_if from the STL:
#include <string>
#include <algorithm>
#include <iostream>
#include <cctype>
using namespace std;
bool is_punct(const char& c) {
return ispunct(c);
}
int main() {
string test = "THisisa test;;';';';";
char test2[] = "THisisa test;;';';'; another";
size_t size = sizeof(test2)/sizeof(test2[0]);
replace_if(test.begin(), test.end(), is_punct, ' ');//for C++ strings
replace_if(&test2[0], &test2[size-1], is_punct, ' ');//for c-strings
cout << test << endl;
cout << test2 << endl;
}
This outputs:
THisisa test
THisisa test another
Try this (as you asked for cstring explicitly):
char str1[100] = "str,, ing";
if(ispunct(str1[start]) || isspace(str1[start])) {
strncpy(str1 + start, str1 + start + 1, strlen(str1) - start + 1);
}
Well, doing this just in pure c language, there are more efficient solutions (have a look at #MichaelPlotke's answer for details).
But as you also explicitly ask for c++, I'd recommend a solution as follows:
Note you can use the standard c++ algorithms for 'plain' c-style character arrays also. You just have to place your predicate conditions for removal into a small helper functor and use it with the std::remove_if() algorithm:
struct is_char_category_in_question {
bool operator()(const char& c) const;
};
And later use it like:
#include <string>
#include <algorithm>
#include <iostream>
#include <cctype>
#include <cstring>
// Best chance to have the predicate elided to be inlined, when writing
// the functor like this:
struct is_char_category_in_question {
bool operator()(const char& c) const {
return std::ispunct(c) || std::isspace(c);
}
};
int main() {
static char str1[100] = "str,, ing";
size_t size = strlen(str1);
// Using std::remove_if() is likely to provide the best balance from perfor-
// mance and code size efficiency you can expect from your compiler
// implementation.
std::remove_if(&str1[0], &str1[size + 1], is_char_category_in_question());
// Regarding specification of the range definitions end of the above state-
// ment, note we have to add 1 to the strlen() calculated size, to catch the
// closing `\0` character of the c-style string being copied correctly and
// terminate the result as well!
std::cout << str1 << endl; // Prints: string
}
See this compilable and working sample also here.
As I don't like the accepted answer, here's mine:
#include <stdio.h>
#include <string.h>
#include <cctype>
int main() {
char str[100] = "str,, ing";
int bad = 0;
int cur = 0;
while (str[cur] != '\0') {
if (bad < cur && !ispunct(str[cur]) && !isspace(str[cur])) {
str[bad] = str[cur];
}
if (ispunct(str[cur]) || isspace(str[cur])) {
cur++;
}
else {
cur++;
bad++;
}
}
str[bad] = '\0';
fprintf(stdout, "cur = %d; bad = %d; str = %s\n", cur, bad, str);
return 0;
}
Which outputs cur = 18; bad = 14; str = string
This has the advantage of being more efficient and more readable, hm, well, in a style I happen to like better (see comments for a lengthy debate / explanation).
I want to build a function to easily convert a string containing hex code (eg. "0ae34e") into a string containing the equivalent ascii values and vice versa.
Do I have to cut the Hex string in pairs of 2 values and gue them together again or is there a convenient way to do that?
thanks
Based on binascii_unhexlify() function from Python:
#include <cctype> // is*
int to_int(int c) {
if (not isxdigit(c)) return -1; // error: non-hexadecimal digit found
if (isdigit(c)) return c - '0';
if (isupper(c)) c = tolower(c);
return c - 'a' + 10;
}
template<class InputIterator, class OutputIterator> int
unhexlify(InputIterator first, InputIterator last, OutputIterator ascii) {
while (first != last) {
int top = to_int(*first++);
int bot = to_int(*first++);
if (top == -1 or bot == -1)
return -1; // error
*ascii++ = (top << 4) + bot;
}
return 0;
}
Example
#include <iostream>
int main() {
char hex[] = "7B5a7D";
size_t len = sizeof(hex) - 1; // strlen
char ascii[len/2+1];
ascii[len/2] = '\0';
if (unhexlify(hex, hex+len, ascii) < 0) return 1; // error
std::cout << hex << " -> " << ascii << std::endl;
}
Output
7B5a7D -> {Z}
An interesting quote from the comments in the source code:
While I was reading dozens of programs that encode or decode the
formats here (documentation? hihi:-) I have formulated Jansen's
Observation:
Programs that encode binary data in ASCII are written in such a style
that they are as unreadable as possible. Devices used include
unnecessary global variables, burying important tables in unrelated
sourcefiles, putting functions in include files, using
seemingly-descriptive variable names for different purposes, calls to
empty subroutines and a host of others.
I have attempted to break with this tradition, but I guess that that
does make the performance sub-optimal. Oh well, too bad...
Jack Jansen, CWI, July 1995.
If you want to use a more c++ native way, you can say
std::string str = "0x00f34" // for example
stringstream ss(str);
ss << hex;
int n;
ss >> n;
The sprintf and sscanf functions can already do that for you. This code is an example that should give you an idea. Please go through the function references and the safe alternatives before you use them
#include <stdio.h>
int main()
{
int i;
char str[80]={0};
char input[80]="0x01F1";
int output;
/* convert a hex input to integer in string */
printf ("Hex number: ");
scanf ("%x",&i);
sprintf (str,"%d",i,i);
printf("%s\n",str);
/* convert input in hex to integer in string */
sscanf(input,"%x",&output);
printf("%d\n",output);
}
This question already has answers here:
How do I iterate over the words of a string?
(84 answers)
Closed 3 years ago.
I have the following code that is suppose to split a string and inserting it into an array :
char *t;
char *tmpLine = (char *)line.c_str();
t = strtok(tmpLine, "\t");
int counter = 0;
while(t != NULL) {
tempGrade[counter] = atoi(t);
counter++;
t = strtok(NULL, "\t");
}
but for some reason the last entry of line is ignored and not inserted. also line is :
string line = "1 90 74 84 48 76 76 80 85";
NOTE : the spaces are tabspaces (\t) in the original file.
Your code is so wrong it burns my eyes.
What is so wrong?
char *tmpLine = (char *)line.c_str();
First, you are (unless I'm wrong) casting away the constness of a std::string, which is a very bad idea. Anyway, casting away the constness smells of modification attempt...
t = strtok(tmpLine, "\t");
And here we are...
You are modifying the buffer provided by the std::string, as strtok destroys the string given to it.
Bad idea: You are not the owner of the std::string's internal buffer, so you should not modify it (you don't want to break your string, and provoke a bug, want you?),
Back to the code:
t = strtok(tmpLine, "\t");
Ok, so, you're using strtok, which is not a reentrant function.
I don't know what compiler you're using, but I guess most would have a more safe (i.e. less stupid) alternative. For example, Visual C++ provides strtok_s. If you don't have one provided, then the best solution is to write your own strtok, or use another tokenization API.
In fact, strtok is one of the few counter-examples of "don't reinvent the wheel": In that case, rewriting your own will always be better than the original standard C function, if you have some experience in C or C++.
Ok, but, about the original problem?
Others have provided insight about alternatives, or even the fact the incomplete sample of code you provided seemed complete, so I'll stop here my analysis, and let you consider their answers.
I don't know what your code is for, but it is plain wrong, and would be even if the bug you reported (the missing token) didn't exist. Don't use that on production code.
but can you give me a quick hack around this? because this is not an important project and I would have to change alot to do it like in that question
No, you don't. This does the same thing assuming tempGrade is an int array:
istringstream iss(line);
int counter = copy(istream_iterator<int>(iss),
istream_iterator<int>(),
tempGrade) - tempGrade;
But it would be better to change tempGrade to vector and use the code from the answer Moo-Juice linked to.
As other people mention, I suggest you take the recommended C++ approach to this problem.
But I tried out your code, and I can't tell what is wrong with it. I don't repro your issue on my end:
1
90
74
84
48
76
76
80
85
Perhaps your iteration to print out the loop is too short, or your tempGrade array is too small? Or perhaps your final "tab" isn't really a tab character?
Here's the code I compiled to check this code.
#include<iostream>
int main(int argc, char* argv[])
{
std::string line = "1\t90\t74\t84\t48\t76\t76\t80\t85";
char *t;
char *tmpLine = (char *)line.c_str();
t = strtok(tmpLine, "\t");
int counter = 0;
int tempGrade[9];
while(t != NULL) {
tempGrade[counter] = atoi(t);
counter++;
t = strtok(NULL, "\t");
}
for(int i = 0; i < 9; ++i) {
std::cout << tempGrade[i] << "\n";
}
}
Here is a very simple example showing how to make use of stream extraction operators and vector to do this in a more idiomatic C++ manner:
#include <string>
#include <iostream>
#include <sstream>
#include <vector>
using namespace std;
void parse(string line)
{
stringstream stream(line);
int score;
vector<int> scores;
while (stream >> score)
scores.push_back(score);
for (vector<int>::iterator itr = scores.begin(); itr != scores.end(); ++itr)
cout << *itr << endl;
}
int main(int argc, char* argv[])
{
parse("1\t90\t74\t84\t48\t76\t76\t80\t85");
return 0;
}