Optimize inversing word while capitalize word after space function - c++

void inverse(char *string)
{
int i = 0;
int j = strlen(string) - 1;
char temp;
//Inverse the order
while ( i < j )
{
temp = *( string + i );
*( string + i ) = *( string + j );
*( string + j ) = temp;
i++;
j--;
}
//Capitalize letter after space
while( *string != '\0')
{
if ( *( string ) == ' ' && *( string + 1 ) != ' ')
{
*( ++string ) = toupper( *( string ) );
}
string++;
}
}
I just want to know if there is a better way to make this output, especially produce same output with less lines of code

Some general tips to cleanup and reduce the number of lines of code:
1. Use for loops instead of while loops. This
int i = 0;
while ( i < 10 )
{
// loop body
i++;
}
is better written as
for ( int i = 0; i < 10; i++ )
{
// loop body
}
2. Declare variables when you first use them. For example, the declaration of temp can be done inside the loop.
3. Use array syntax instead of pointer arithmetic. For example, this
*( string + i ) = *( string + j );
is better written as
string[i] = string[j];
With that in mind the code to reverse the string could be written as
for ( int i = 0, j = strlen(string)-1; i < j; i++, j-- )
{
char temp = string[i];
string[i] = string[j];
string[j] = temp;
}
As mentioned in the comments, the code to capitalize the first letter of words has some issues. The algorithm in the question advances the string pointer until it points to a space character, and then capitalizes the character at address string+1.
An alternative algorithm keeps a copy of the previous character. If the previous character was a space, then the current character is capitalized. Here's an implementation of that algorithm:
for ( char oldc = ' '; *string != '\0'; string++ )
{
if ( oldc == ' ' )
*string = toupper(*string);
oldc = *string;
}
Note that this will capitalize the first character in the string, and any character that follows a space. If you specifically only want to capitalize characters that follow a space, and not the first character, then change the initialization of oldc so that is doesn't start as a space, e.g.
for ( char oldc = 'a'; *string != '\0'; string++ )

Oh, you can optimize it greatly. Let's review the logic first.
We need to have each character that follows the space be uppercased in the result string. What does that mean? That means that in the original string such characters are located before the space. Thus, we have the criteria to find those in the original string and actually make them uppercase even before swapping. This renders second loop unnessessary -- we can do all in one.
Here's the example how it can be implemented:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
void transform(char* s)
{
char temp;
char* endPtr = s + strlen(s) - 1;
while (s < endPtr)
{
/*
converting left hand part like 'c ' to 'C '
so that after swap it turns to ' C'
*/
if (!isspace(*s) && isspace(*(s + 1)))
*s = toupper(*s);
/* same to the right side */
if (isspace(*endPtr) && !isspace(*(endPtr - 1)))
*(endPtr - 1) = toupper(*(endPtr - 1));
/* swap */
temp = *s;
*s = *endPtr;
*endPtr = temp;
s++;
endPtr--;
}
}
int main(void)
{
char str[] = "this is a demo string! the one and only real demo string. crazy example ";
transform(str);
printf("'%s'\n", str);
return 0;
}

You can remove the temp variable while reversing using xor operation
int end= strlen(string)-1;
int start = 0;
while( start<end )
{
string[start] ^= string[end];
string[end] ^= string[start];
string[start]^= string[end];
++start;
--end;
}
To capitalized each letter, try with code below:
char* reversestr = *string;
while( *reversestr != '\0')
{
if (i==0 || *(reversestr - 1) ==' ' && *reversestr >= 'a' && *reversestr <= 'z')
*reversestr = toupper(*reversestr);
reversestr++;
}
I am not tested the code yet, but I think it should work.

Related

How to get the number of words from a line from a text file

I am trying to get the number of words from a line in a text file. I used .getline() in order to extract a line from the entire text file. The code is:
#include <iostream>
#include <iomanip>
#include <fstream>
int main()
{
char const* filename = "duck.txt";
std::ifstream ifs{ filename };
constexpr size_t MAX_LINE_LEN{ 2048 };
char line[MAX_LINE_LEN];
int lineCount = 0;
int totalWordCount = 0;
int totalByteCount = 0;
while (ifs.getline(line, MAX_LINE_LEN-1))
{
int lineWord = 0;
char* q = &line[0];
if (ifs.eof())
{
lineCount--;
totalByteCount--;
}
while (*q != '\0')
{
q++;
totalByteCount++;
}
totalByteCount++;
if (*q == '\0')
{
lineCount++;
}
int i = 0;
int j = 0;
while (line[i] != '\0' && line[j] != '\0')
{
while (line[i] == ' ')
{
i++;
}
j = i;
while (line[j] != ' ')
{
j++;
}
lineWord++;
j = i;
}
totalWordCount += lineWord;
}
std::cout << "Total Lines: " << lineCount << '\n' << "Total Words: " << totalWordCount << '\n' << "Total Bytes: " << totalByteCount;
}
But the only important part is:
int i = 0;
int j = 0;
while (line[i] != '\0' && line[j] != '\0')
{
while (line[i] == ' ')
{
i++;
}
j = i;
while (line[j] != ' ')
{
j++;
}
lineWord++;
j = i;
}
totalWordCount += lineWord;
I'm trying to read the line character by character until I reach a non-whitespace character after which I'll assign that subscript to i. Then, I'll set j to the subscript of the first whitespace encountered after the character of line[i]. If j finds a whitespace, then there is a word. If j reaches '\0', then the line has ended and I end the while loop. When I try to compile and run this, the compiler just displays nothing. What am I doing wrong? Also, I can't add anymore header files
In your original code:
int i = 0;
int j = 0;
while (line[i] != '\0' && line[j] != '\0')
{
while (line[i] == ' ') // you do not check for end of string character?
{
i++;
}
j = i; // here i = j = beginning of word.
// since you rewind to beginning of the word below,
// your program keeps repeating this loop endlessly
while (line[j] != ' ') // this loop could easily run for quite a while.
// until it seg-faults
{
j++;
}
lineWord++;
j = i; // BUG Here! you're 'rewinding' j to to beginning of the word.
// you loop back and keep counting the same word over and over.
}
totalWordCount += lineWord;
What is the purpose of i and j ? Wouldn't the code be simpler and easier to read and maintain using a single pointer? Or a single index? This kind of algorithm is where a pointer would excel, though, as the only arithmetic pointer operation needed is increment.
As in:
const char* p = &line[0];
int word_count = 0;
line[MAX_LINE_LEN - 1] = 0; // making sure the code below stays within boundaries.
for(;;)
{
// skip to next word
while (*p && *p == ' ') ++p; // stay within the string by testing for zero.
if (!*p)
break; // done!
// since p now points to the beginning of a word, we've got one
++word_count;
// skip to end of word
while (*p && *p != ' ') ++p;
}
This is all fine, for most cases but there could be some exceptional typos in the text, like "hello, world !", where the last punctuation would be counted as a word. There is also the problem of horizontal tabs, which could also be counted erroneously as words.
To cover these cases, you should test for valid characters for words, instead of for space, which is a rather vague concept.
Without using library calls, You'd need to define what constitutes spaces and punctuation, either with a constant, or with a function.
Substituting the test for space with a more targeted test, using isalnum() to check for alpha or numeric characters:
for(;;)
{
// skip to next word
while (*p && !std::isalnum(*p & 0xFF)) ++p;
if (!*p)
break; // done!
++word_count;
// skip to end of word
while (*p && std::isalnum(*p & 0xFF)) ++p;
}
Beware of function of the isalnum(), isalpha().. family, they define their input as an int, the mask ensures that characters in the 128-255 range are not sign-extended aand are passed correctly as positive values.

Why can't I change the pointer's content when in a loop?

I'm given a string and i need to display all strings that can be formed removing one letter at a time.
For example,for "abbc" i should display "bbc"
"abc"
"abc"
and "abb"
Here is my code:
int main()
{
char s[41]="abbc",*p;
int n=strlen(s);
p=s;
int i=0;
while(i<n)
{
strcpy(p+i,p+i+1);
cout<<p<<" ";
i++;
strcpy(p,s);
}
return 0;
}
It keeps showing bbc bc bc bc as if strcpy(p,s); wasn't wrote. Why can't I give a value to the pointer p in a loop?
To output substrings of a string according to your description there is no need to change the original string.
This call
strcpy(p+i,p+i+1);
has 1) undefined behavior (because the ranges are overlapped) 2) and changes the original string.
This call
strcpy(p,s);
does not make sense because the string is copied into itself due to this assignment
p=s;
The task can be done using ordinary for-loops. For example (a C++ program)
#include <iostream>
int main()
{
const char *s = "abbc";
for ( size_t i = 0; s[i] != '\0'; i++ )
{
for ( size_t j = 0; s[j] != '\0'; j++ )
{
if ( j != i ) std::cout << s[j];
}
std::cout << '\n';
}
return 0;
}
The program output is
bbc
abc
abc
abb
Or (a C program)
#include <stdio.h>
int main(void)
{
const char *s = "abbc";
for ( size_t i = 0; s[i] != '\0'; i++ )
{
for ( size_t j = 0; s[j] != '\0'; j++ )
{
if ( j != i ) putchar( s[j] );
}
putchar( '\n' );
}
return 0;
}
Here is a pure C version. It removes the last character from the string and stores it. After that it replaces each character with the one that came after it, until all have been replaced. At the end the string will contain the substring starting at the second character.
#include <string.h>
#include <stdio.h>
int main()
{
char s[41] = "abbc";
int n;
char c, tmp;
n = strlen(s);
c = 0;
while( n > 0 )
{
n--;
tmp = c;
c = s[n];
s[n] = tmp;
printf( "%s\n", s );
}
return 0;
}
p and s is pointing to the same memory. The memory is modified at each loop, so after the first loop n is no longer valid as the length of the string.
loop 1: p & s = "abbc", i=0, n=4
p+0 points to "abbc", p+1 points to "bbc"
value of s after strcpy(p+0, P+1) is "bbc"
loop 2: p & s = "bbc", i=1, n=4
p+1 points to "bc", p+1+1 points to "c"
value of s after strcpy(p+1, P+2) is "bc"
loop 3: p & s = "bc", i=2, n=4
p+2 points to null or "", p+3 points to something after string
value of s after strcpy(p+2, p+3) is undifined but likely remaining "bc"
loop 4: nothing treally change from above as you now are outside string

Insert a character at a specific position in a char array

As the title says I want to insert a character after another one in a char array using the C - style not by using the library string, and please avoid using buffer functions as well, only the basic ones :) .
char sentence[100],*p = NULL;
cin.get(sentence,100);
char replaceChar; // after What character should we insert
cin>>replaceChar;
char insertingChar; // the character we are inserting after the replaceChar;
cin >> insertingChar ;
p = strchr(sentence , replace);
while(p != NULL){
//and this is I could think of ...
}
So let`s say that we have this sentence : "I want apples", the replaceChar = a, and the insertingChar = '*';
The result should be: "I wa*nt a*pples" .
This will shift chars to the right, making room for insertion.
void rshift( char *s ){
int n = strlen( s);
s[ n + 1] = 0;
while( n ){
s[ n ] = s[ n-1 ];
n--;
}
}
int main(){
char *p = strchr(sentence , replace);
if( p ) {
p++; // insert after
rshift( p );
*p = insertingChar;
}
}

Creating a word shifter

For an assignment, I am working on creating a word shifter in C++. I have little to no experience with C++ so it has been very difficult. I think I am really close but just missing some syntax that is part of C++. Any help would be appreciated greatly.
string s = phrase;
int length = s.length();
//find length of input to create a new string
string new_phrase[length];
//create a new string that will be filled by my for loop
for (int i=0; i<length; i++)
//for loop to go through and change the letter from the original to the new and then put into a string
{
int letter = int(s[i]);
int new_phrase[i] = letter + shift;
//this is where I am coming up with an error saying that new_phrase is not initialized
if (new_phrase[i] > 122)
//make sure that it goes back to a if shifting past z
{
new_phrase[i] = new_phrase[i] - 26;
}
}
cout << new_phrase<< endl;
Considering your syntax,I wrote an example for you.Besides,it is conventional
to write comment before it's relevant code.
#include <iostream>
#include <string>
using namespace std;
int main()
{
//test value;
int shift = 3;
string s = "hello string";
//find length of input to create a new string
int length = s.length();
//create a new string.it's length is same as 's' and initialized with ' ';
string new_phrase(length, ' ');
for (int i=0; i<length; i++)
{
//no need to cast explicitly.It will be done implicitly.
int letter = s[i];
//It's assignment, not declaration
new_phrase[i] = letter + shift;
//'z' is equal to 126.but it's more readable
if (new_phrase[i] > 'z')
{
new_phrase[i] = new_phrase[i] - ('z' - 'a' + 1);
}
}
cout << new_phrase<< endl;
}
This should work.
// must be unsigned char for overflow checking to work.
char Shifter(unsigned char letter)
{
letter = letter + shift;
if (letter > 'z')
letter = letter - 26;
return letter;
}
// :
// :
string new_phrase = phrase; // mainly just allocating a string the same size.
// Step throught each char in phrase, preform Shifter on the char, then
// store the result in new_phrase.
std::transform(phrase.begin(), phrase.end(), new_phrase.begin(), Shifter);
cout << new_phrase<< endl;
UPDATE: made letter unsigned, so the overflow check works.
Try and investigate this code
#include <iostream>
#include <string>
#include <cctype>
void ShiftRight( std::string &s, std::string::size_type n )
{
if ( n >= 'Z' - 'A' + 1 ) return;
for ( char &c : s )
{
bool lower_case = std::islower( c );
c = std::toupper( c );
c = ( c + n -'A' ) % ('Z' -'A' + 1 ) + 'A';
if ( lower_case ) c = std::tolower( c );
}
}
int main()
{
std::string s( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" );
std::cout << s << std::endl << std::endl;
for ( std::string::size_type i = 1; i <= 'Z' -'A' + 1; i++ )
{
std::str std::string s( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" );
ShiftRight( s, i );
std::cout << s << std::endl;
}
return 0;
}
The output is
ABCDEFGHIJKLMNOPQRSTUVWXYZ
BCDEFGHIJKLMNOPQRSTUVWXYZA
CDEFGHIJKLMNOPQRSTUVWXYZAB
DEFGHIJKLMNOPQRSTUVWXYZABC
EFGHIJKLMNOPQRSTUVWXYZABCD
FGHIJKLMNOPQRSTUVWXYZABCDE
GHIJKLMNOPQRSTUVWXYZABCDEF
HIJKLMNOPQRSTUVWXYZABCDEFG
IJKLMNOPQRSTUVWXYZABCDEFGH
JKLMNOPQRSTUVWXYZABCDEFGHI
KLMNOPQRSTUVWXYZABCDEFGHIJ
LMNOPQRSTUVWXYZABCDEFGHIJK
MNOPQRSTUVWXYZABCDEFGHIJKL
NOPQRSTUVWXYZABCDEFGHIJKLM
OPQRSTUVWXYZABCDEFGHIJKLMN
PQRSTUVWXYZABCDEFGHIJKLMNO
QRSTUVWXYZABCDEFGHIJKLMNOP
RSTUVWXYZABCDEFGHIJKLMNOPQ
STUVWXYZABCDEFGHIJKLMNOPQR
TUVWXYZABCDEFGHIJKLMNOPQRS
UVWXYZABCDEFGHIJKLMNOPQRST
VWXYZABCDEFGHIJKLMNOPQRSTU
WXYZABCDEFGHIJKLMNOPQRSTUV
XYZABCDEFGHIJKLMNOPQRSTUVW
YZABCDEFGHIJKLMNOPQRSTUVWX
ZABCDEFGHIJKLMNOPQRSTUVWXY
ABCDEFGHIJKLMNOPQRSTUVWXYZ
As for your code then it of course is wrong. You have not to define an array of strings. And do not use magic numbers as for example 122.
Also you may include in my code a check that a next symbol is an alpha symbol.

remove commas from string

I created a program in C++ that remove commas (,) from a given integer. i.e. 2,00,00 would return 20000. I am not using any new space. Here is the program I created:
void removeCommas(string& str1, int len)
{
int j = 0;
for (int i = 0; i < len; i++)
{
if (str1[i] == ',')
{
continue;
}
else
{
str1[j] = str1[i];
j++;
}
}
str1[j] = '\0';
}
void main()
{
string str1;
getline(cin, str1);
int i = str1.length();
removeCommas(str1, i);
cout << "the new string " << str1 << endl;
}
Here is the result I get:
Input : 2,000,00
String length =8
Output = 200000 0
Length = 8
My question is that why does it show the length has 8 in output and shows the rest of string when I did put a null character. It should show output as 200000 and length has 6.
Let the standard library do the work for you:
#include <algorithm>
str1.erase(std::remove(str1.begin(), str1.end(), ','), str1.end());
If you don't want to modify the original string, that's easy too:
std::string str2(str1.size(), '0');
str2.erase(std::remove_copy(str1.begin(), str1.end(), str2.begin(), ','), str2.end());
You need to do a resize instead at the end.
Contrary to popular belief an std::string CAN contain binary data including 0s. An std::string 's .size() is not related to the string containing a NULL termination.
std::string s("\0\0", 2);
assert(s.size() == 2);
The answer is probably that std::strings aren't NUL-terminated. Instead of setting the end+1'th character to '\0', you should use str.resize(new_length);.
Edit: Also consider that, if your source string has no commas in it, then your '\0' will be written one past the end of the string (which will probably just happen to work, but is incorrect).
The std::srting does not terminate with \0, you are mixing this with char* in C. So you should use resize.
The solution has already been posted by Fred L.
In a "procedural fashion" (without "algorithm")
your program would look like:
void removeStuff(string& str, char character)
{
size_t pos;
while( (pos=str.find(character)) != string::npos )
str.erase(pos, 1);
}
void main()
{
string str1;
getline(cin, str1);
removeStuff(str1, ',');
cout<<"the new string "<<str1<<endl;
}
then.
Regards
rbo
EDIT / Addendum:
In order to adress some efficiency concerns of readers,
I tried to come up with the fastest solution possible.
Of course, this should kick in on string sizes over
about 10^5 characters with some characters to-be-removed
included:
void fastRemoveStuff(string& str, char character)
{
size_t len = str.length();
char *t, *buffer = new char[len];
const char *p, *q;
t = buffer, p = q = str.data();
while( p=(const char*)memchr(q, character, len-(p-q)) ) {
memcpy(t, q, p-q);
t += p-q, q = p+1;
}
if( q-str.data() != len ) {
size_t tail = len - (q-str.data());
memcpy(t, q, tail);
t += tail;
}
str.assign(buffer, t-buffer);
delete [] buffer;
}
void main()
{
string str1 = "56,4,44,55,5,55"; // should be large, 10^6 is good
// getline(cin, str1);
cout<<"the old string " << str1 << endl;
fastRemoveStuff(str1, ',');
cout<<"the new string " << str1 << endl;
}
My own procedural version:
#include <string>
#include <cassert>
using namespace std;
string Remove( const string & s, char c ) {
string r;
r.reserve( s.size() );
for ( unsigned int i = 0; i < s.size(); i++ ) {
if ( s[i] != c ) {
r += s[i];
}
}
return r;
}
int main() {
assert( Remove( "Foo,Bar,Zod", ',' ) == "FooBarZod" );
}
Here is the program:
void main()
{
int i ;
char n[20] ;
clrscr() ;
printf("Enter a number. ") ;
gets(n) ;
printf("Number without comma is:") ;
for(i=0 ; n[i]!='\0' ; i++)
if(n[i] != ',')
putchar(n[i]) ;
getch();
}
For detailed description you can refer this blog: http://tutorialsschool.com/c-programming/c-programs/remove-comma-from-string.php
The same has been discussed in this post: How to remove commas from a string in C
Well, if youre planing to read from a file using c++. I found a method, while I dont think thats the best method though, but after I came to these forums to search for help before, I think its time to contribute with my effort aswell.
Look, here is the catch, what I'm going to present you is part of the source code of the map editor Im building on right now, that map editor obviously has the purpose to create maps for a 2D RPG game, the same style as the classic Pokemon games for example. But this code was more towards the development of the world map editor.
`int strStartPos = 0;
int strSize = 0;
int arrayPointInfoDepth = 0;
for (int x = 0; x < (m_wMapWidth / (TileSize / 2)); x++) {
for (int y = 0; y < (m_wMapHeight / (TileSize / 2)); y++) {
if (ss >> str) {
for (int strIterator = 0; strIterator < str.length(); strIterator++) {
if (str[strIterator] == ',') {`
Here we need to define the size of the string we want to extract after the previous comma and before the next comma
`strSize = strIterator - strStartPos;`
And here, we do the actual transformation, we give to the vector that is a 3D vector btw the string we want to extract at that moment
`m_wMapPointInfo[x][y][arrayPointInfoDepth] = str.substr(strStartPos, strSize);`
And here, we just define that starting position for the next small piece of the string we want to extract, so the +1 means that after the comma we just passed
strStartPos = strIterator + 1;
Here, well since my vector has only 6 postions that is defined by WorldMapPointInfos we need to increment the third dimension of the array and finally do a check point where if the info has arrived the number 6 then break the loop
arrayPointInfoDepth++;
if (arrayPointInfoDepth == WorldMapPointInfos) {
strStartPos = 0;
arrayPointInfoDepth = 0;
break;
}
}
}
}
}
}
Either way on my code, think abt that the vector is just a string, thats all you need to know, hope this helps though :/
Full view:
int strStartPos = 0;
int strSize = 0;
int arrayPointInfoDepth = 0;
for (int x = 0; x < (m_wMapWidth / (TileSize / 2)); x++) {
for (int y = 0; y < (m_wMapHeight / (TileSize / 2)); y++) {
if (ss >> str) {
for (int strIterator = 0; strIterator < str.length(); strIterator++) {
if (str[strIterator] == ',') {
strSize = strIterator - strStartPos;
m_wMapPointInfo[x][y][arrayPointInfoDepth] = str.substr(strStartPos, strSize);
strStartPos = strIterator + 1;
arrayPointInfoDepth++;
if (arrayPointInfoDepth == WorldMapPointInfos) {
strStartPos = 0;
arrayPointInfoDepth = 0;
break;
}
}
}
}
}
}