I am trying to take a text file with names (ex: john doe) and fin the first name and last name. Then, I want to take these two char arrays and concatenate them together using pointers. The code that is commented out is working code that takes the two char arrays and puts them into a single char array ie concatenating them together. This project requires that I use pointers, and that I use char arrays I am not asking for you to do it for me, but please help me realize what I am doing wrong. Thanks
EDIT: the error I am getting is a seg fault..so Im thining my playerPtr is going out of bounds somewhere??
void readPlayer(char *finName2, player *playerPtr)
{
player *playerHome = playerPtr;
ifstream fin;
char *tempfName= new char[20];
char *templName= new char[20];
char *tempName= new char[20];
char *tempNameHome = tempName;
fin.open(finName2);
if(!fin.good())
{
cout << "Error with player file!" << endl;
}
else
{
fin >> tempfName;
fin >> templName; //prime file
cout << tempfName << templName;
while(fin.good())
{
for(int i =0;i<5;i++)
{
//find the length
//int index =0, length=0;
while(*tempfName != '\0')
//while(tempfName[length] != '\0')
{
tempfName++;
}
strcopy(tempName,tempfName);
//now add space after first name
*tempName = ' ';
tempName++;
//tempfName[length] = ' ';
//tempfName++;
//length++;
while(*templName != '\0')
//while(templName[index] != '\0')
{
templName++;
//tempfName[length] = templName[index];
//length++;
//index++;
}
strcopy(tempName,templName);
//tempName++;
//tempfName[length]='\0';
strcopy((*playerPtr).name,tempName);
playerPtr++;
fin >> tempfName;
fin >> templName;
}
}
}
delete[] tempfName;
delete[] templName;
delete[]tempName;
}
Your tempfName & templName are incremented all the time and they move beyond their allocated memory. you need to reset their position.
Plus I can see that the
fin >> tempfName;
fin >> templName;
is inside the for loop, which means fin.Good is only checked once every 5 times.
Problems that I see (mentioned in comments too):
Incrementing tempFName and tempLName in loops
while(*tempfName != '\0')
{
tempfName++;
}
strcopy(tempName,tempfName);
At the end of the above loop, tempFName points to the end of the string - it points to the terminating null character. strcopy should copy nothing to tempName.
You have the same problem with the loop:
while(*templName != '\0')
{
templName++;
}
strcopy(tempName,templName);
Setting the value of *tempName after the first loop
//now add space after first name
*tempName = ' ';
tempName++;
This will be valid only if tempName points to the end of the copied string after the call to strcopy. If not, you are just setting the value of the first character in tempName to ' '. Incrementing tempName makes sense only of tempName points to the end of the copied string. Otherwise, it points to the second character.
As a result of the above errors, your code is subject to errors caused by out of bound memory access after the first iteration of the for loop. Nothing after that can be relied upon to behave in a reasonable manner.
I suggest the following changes to fix the above errors.
Don't increment the variables tempFName and tempLName at all
You don't need to at all.
Remove the lines:
while(*tempfName != '\0')
{
tempfName++;
}
just use:
strcopy(tempName,tempfName);
Use a temporary pointer to go to the end of tempName
After the first call to strcopy, use:
char* temp = tempName;
while ( *temp != '\0' ) ++temp;
*temp = ' ';
++temp;
*temp = '\0';
Use the temporary pointer for the second strcopy
Remove the lines:
while(*templName != '\0')
{
templName++;
}
Replace the line:
strcopy(tempName,templName);
with
strcopy(temp,tempfName);
Alternate strategy
If you implement your version of strcat, you can simply use:
tempName[0] = '\0';
strcat(tempName, tempFName);
strcat(tempName, " ");
strcat(tempName, tempLName);
That will remove much of the clutter in the for loop.
Related
I was working on a system that split a sentence to a 2D pointer.
I don't wanna use any kind of library or another ways like string, because I want to practice pointers and learn them.
char** sscanf(char* hstring)
{
int count = 0;
char* current = hstring;
while (*current)
{
if (*current == ' ')
{
count++;
}
while (*current == ' ')
{
current++;
}
if (*current)
break;
current++;
}
char** result = new char*[count];
current = hstring;
char* nstr = new char;
int c = 0, i = 0;
while (*current)
{
if (!*current) break;
cout << "t1";
if (*current == ' ')
{
*(++result) = nstr;
nstr = nullptr;
nstr = new char;
}
cout << "t2";
while (*current != '/0' && *current == ' ')
{
current++;
}
cout << "t3";
while (*current != '/0' && *current != ' ')
{
if (!*current) break;
*(++nstr) = *current;
current++;
}
cout << "t4";
*nstr = '/0';
cout << "t5";
}
return result;
}
But it's very strange, sometimes redirects me to
static size_t __CLRCALL_OR_CDECL length(_In_z_ const _Elem * const _First) _NOEXCEPT // strengthened
{ // find length of null-terminated string
return (_CSTD strlen(_First));
}
with error: Acces Violation, other times, choose a random line and call it Acces Breakout(sorry if I spelled wrong)
What I want from you is not to repair my code simply, I want some explanations, because I want to learn this stuff.
First, some advice
I understand that you are making this function as an exercise, but being C++ I'd like to warn you that things like new char*[count] are bad practices and that's why std::vector or std::array were created.
You seem confused about how dynamic allocation works. The statement char* nstr = new char; will create just one byte (char) in heap memory, and nothing is guaranteed to be adjacent to it. This means that ++nstr is a "invalid" operation, I mean, it's making the nstr point to the next byte after the allocated one, which can be some random invalid location.
There is a whole lot of other dangerous operations in your code, like calling new several times (which reserves memory) and not calling delete on them when you no longer use the reserved memory (aka. memory leaks). Having said that, I strongly encourage you to study this subject, for example starting with the ISO C++ FAQ on memory management.
Also, before digging into pointers and dynamic allocation, you should be more confortable with statements and flow control. I say this because I see some clear misunderstandings, like:
while (*current) {
if (!*current) break;
...
}
The check inside the if statement will certainly be false, because the while check is executed just before it and guarantees that the opposite condition is true. This means that this if is never evaluated to true and it's completely useless.
Another remark is: don't name your functions the same as standard libraries ones. sscanf is already taken, choose another (and more meaningful) one. This will save you some headaches in the future; be used to name your own functions properly.
A guided solution
I'm in a good mood, so I'll go through some steps here. Anyway, if someone is looking for an optimized and ready to go solution, see Split a String in C++.
0. Define the steps
Reading your code, I could guess some of your desired steps:
char** split_string(char* sentence)
{
// Count the number of words in the sentence
// Allocate memory for the answer (a 2D buffer)
// Write each word in the output
}
Instead of trying to get them right all at once, why don't you try one by one? (Notice the function's and parameter's names, clearer in my opinion).
1. Count the words
You could start with a simple main(), testing your solution. Here is mine (sorry, I couldn't just adapt yours). For those who are optimization-addicted, this is not an optimized solution, but a simple snippet for the OP.
// I'll be using this header and namespace on the next snippets too.
#include <iostream>
using namespace std;
int main()
{
char sentence[] = " This is my sentence ";
int n_words = 0;
char *p = sentence;
bool was_space = true; // see logic below
// Reading the whole sentence
while (*p) {
// Check if it's a space and advance pointer
bool is_space = (*p++ == ' ');
if (was_space && !is_space)
n_words++; // count as word a 'rising edge'
was_space = is_space;
}
cout << n_words;
}
Test it, make sure you understand why it works. Now, you can move to the next step.
2. Allocate the buffer
Well, you want to allocate one buffer for each word, so we need to know the size of each one of them (I'll not discuss whether or not this is a good approach to the split sentence problem..). This was not calculated on the previous step, so we might do it now.
int main()
{
char sentence[] = " This is my sentence ";
///// Count the number of words in the sentence
int n_words = 0;
char *p = sentence;
bool was_space = true; // see logic below
// Reading the whole sentence
while (*p) {
// Check if it's a space and advance pointer
bool is_space = (*p++ == ' ');
if (was_space && !is_space)
n_words++; // count as word a 'rising edge'
was_space = is_space;
}
///// Allocate memory for the answer (a 2D buffer)
// This is more like C than C++, but you asked for it
char **words = new char*[n_words];
char *ini = sentence; // the initial char of each word
for (int i = 0; i < n_words; ++i) {
while (*ini == ' ') ini++; // search next non-space char
char *end = ini + 1; // pointer to the end of the word
while (*end && *end != ' ') end++; // search for \0 or space
int word_size = end - ini; // find out the word size by address offset
ini = end; // next for-loop iteration starts
// at the next word
words[i] = new char[word_size]; // a whole buffer for one word
cout << i << ": " << word_size << endl; // debugging
}
// Deleting it all, one buffer at a time
for (int i = 0; i < n_words; ++i) {
delete[] words[i]; // delete[] is the syntax to delete an array
}
}
Notice that I'm deleting the allocated buffers inside the main(). When you move this logic to your function, this deallocation will be performed by the caller of the function, since it will probably use the buffers before deleting them.
3. Assigning each word to its buffer
I think you got the idea. Assign the words and move the logic to the separated function. Update your question with a Minimal, Complete, and Verifiable example if you still have troubles.
I know this is a Q&A forum, but I think this is already a healthy answer to the OP and to others that may pass here. Let me know if I should answer differently.
I need to make a program that capitalizes the first character of each sentence in a string. For instance, if the string argument is “hello. my name is Joe. what is your name?” the function should manipulate the string so it contains “Hello. My name is Joe. What is your name?” I'm not sure what I am doing wrong. Any suggestions? Here is my code:
#include<iostream>
#include<cctype>
#include<cstdlib>
using namespace std;
void capitalize(char sentence[], int const SIZE);
int main()
{
const int SIZE = 1024;
char sentence[SIZE];
cout << "Enter a string: " << endl << endl;
cin.getline(sentence, SIZE);
capitalize(sentence, SIZE);
system("pause");
return(0);
}
void capitalize(char sentence[], int SIZE)
{
char *strPtr;
int count = 0;
sentence[0] = toupper(sentence[0]);
for (int i = 0; i < SIZE; i++)
{
strPtr = strstr(sentence[i], ".");
if (*strPtr == '.')
{
*strPtr = toupper(*strPtr);
}
}
while (sentence[count] != '\0')
{
cout << sentence[count];
count++;
}
}
#include <cstring> // need this for strstr()
void capitalize(char sentence[], int SIZE)
{
char *strPtr;
int count = 0;
sentence[0] = toupper(sentence[0]);
for (int i = 0; i < SIZE; i++)
{
strPtr = strstr(&sentence[i], ".");
//strPtr returns the pointer to
//the first occurence of "." after sentence[i]
if(strPtr==NULL) break;
if (*strPtr == '.')
{
// you really dont want to do this
//*strPtr = toupper(*strPtr);
// put the suitable code here and everything will work
}
}
//why the while loop? and count?
while (sentence[count] != '\0')
{
cout << sentence[count];
count++;
}
}
What you were doing was to capitalize "." but clearly you want the next character to be capitalized. So write that part of code yourself as you'll find it more rewarding.
First, as mentioned in the comments, you're not including cstring. Second, you're calling strstr on sentence[i], which is a char. You want sentence + i which is a char*. That'll fix your syntax errors.
For logical error, it looks like you're trying toupper the period.
strPtr = strstr(sentence[i], "."); should find the first period in the string starting at i (inclusive). Then you check if strstr found anything (if not it would return null. If it's found the sequence you uppercase strPtr, but strPtr still points at the first character of the target string, that is '.'. You should be looking for the target string ". " then incrementing one past that to find the first letter of the next sentence. Unfortunately there's no safe way of doing that with strstr since it doesn't tell you how far into the string it looked, so it's possible the string simply ends with ". " and one past that falls off the array. You're either going to need to iterate through the array manually, looking for '.' then checking past that, or use std::find instead.
Why does my program crash when I try to put delimiter into vector::operators?I'm trying to make a working calculator and I need to find all the operators of the input string in order. All the necessary libraries were included, and the code worked when I was only using 2 numbers and 1 operator. Is the problem that I used the loop incorrectly? I also have a similar function to find the numbers that works and crashes in the same spots.
vector<char>operators;
int main()
{
string input;
cin >> input;
find_operators(input);
}
void find_operators(string X)
{
char * cX = new char[X.length()];
strcpy(cX, X.c_str());
char * delimiter = strpbrk(cX,"+*-/");
operators.push_back(*delimiter); //this worked
while (delimiter != NULL)
{
delimiter = strpbrk(delimiter+1, "+-*/");
cout << "OK"; //makes it to this point then crashes
operators.push_back(*delimiter); //this doesn't work
}
delete[] cX;
}
You have a couple issues with your code. First:
char * cX = new char[X.length()];
strcpy(cX, X.c_str());
Is incorrect as you are not accounting for the null terminator that you need in cX. length() only returns the number of character in the string and does not count the null terminator. To fix that all you need to do is:
char * cX = new char[X.length() + 1];
strcpy(cX, X.c_str()); // ^^^^ add one to the size
The second issue you have is in:
while (delimiter != NULL)
{
delimiter = strpbrk(delimiter+1, "+-*/");
cout << "OK"; //makes it to this point then crashes
operators.push_back(*delimiter); //this doesn't work
}
You check if delimiter is not null and then you reassign it using strpbrk(). If strpbrk() returns NULL then operators.push_back(*delimiter) is going to fail because you are dereferencing a null pointer. You should be able to change your code to the following to get it to work:
//...
char * delimiter = strpbrk(cX,"+*-/"); // get operator
while (delimiter != NULL) // keep looping while we have an operator
{
operators.push_back(*delimiter); // add the operator to the vector
delimiter = strpbrk(delimiter+1, "+-*/"); // find the next operator
}
//...
I basically have a txt file that looks like this...
High Score: 50
Player Name: Sam
Number Of Kills: 5
Map
Time
I want to store everything before the : or whitespace after Map and Time into one array and everything after in another. For both Map and Time, there is nothing after and so I want to store the whitespace as null.
So far, I have managed to read and store all this information into a temp array. However, it is separating that I am having trouble with. This is my code:
istream operator >> (istream &is, Player &player)
{
char **temp;
char **tempNew;
char lineInfo[200]
temp = new char*[5];
tempNew = new char*[5];
for (int i=0; i<5; i++)
{
temp[i] = new char[200];
is.getline(lineInfo, sizeof(lineInfo));
int length = strlen(lineInfo);
for (int z=0; z < length; z++)
{
if(lineInfo[z] == '= ' ){ //HOW DO I CHECK IF THERE IS NOTHING AFTER THE LAST CHAR
lineInfo [length - (z+1)] = lineInfo [length];
cout << lineInfo << endl;
strncpy(temp[i], lineInfo, sizeof(lineInfo));
}
else{
tempNew[i] = new char[200];
strncpy(tempNew[i], lineInfo, sizeof(lineInfo));
}
}
}
If what you need is to find ':'
#include <cstring>
and just
auto occurance = strstr(string, substring);
Documentation here.
if occurance is not a null ptr, then see if occurance is at the end of the line from get line. If not, your value is everything after that :
Much easier with std::string.
// Read high score
int high_score;
my_text_file.ignore(10000, ':');
cin >> high_score;
// Read player name
std::string player_name;
my_text_file.ignore(10000, ':');
std::getline(my_text_file, player_name);
// Remove spaces at beginning of string
std::string::size_type end_position;
end_position = player_name.find_first_not_of(" \t");
if (end_position != std::string::npos)
{
player_name.erase(0, end_position - 1);
}
// Read kills
unsigned int number_of_kills = 0;
my_text_file.ignore(':');
cin >> number_of_kills;
// Read "Map" line
my_text_file.ignore(10000, '\n');
std::string map_line_text;
std::getline(my_text_file, map_line_text);
// Read "Text" line
std::string text_line;
std::getline(my_text_file, text_line);
If you insist on using C-style strings (arrays of char), you will have to use more complex and less safe functionality. Look up the following functions:
fscanf, strchr, strcpy, sscanf
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 11 years ago.
char **findwords(char *str);
int main()
{
int test;
char words[100]; //an array of chars to hold the string given by the user
char **word; //pointer to a list of words
int index = 0; //index of the current word we are printing
char c;
cout << "die monster !";
//a loop to place the charecters that the user put in into the array
do {
c = getchar();
words[index] = c;
} while (words[index] != '\n');
word = findwords(words);
while (word[index] != 0) //loop through the list of words until the end of the list
{
printf("%s\n", word[index]); // while the words are going through the list print them out
index ++; //move on to the next word
}
//free it from the list since it was dynamically allocated
free(word);
cin >> test;
return 0;
}
char **findwords(char *str)
{
int size = 20; //original size of the list
char *newword; //pointer to the new word from strok
int index = 0; //our current location in words
char **words = (char **)malloc(sizeof(char *) * (size +1)); //this is the actual list of words
/* Get the initial word, and pass in the original string we want strtok() *
* to work on. Here, we are seperating words based on spaces, commas, *
* periods, and dashes. IE, if they are found, a new word is created. */
newword = strtok(str, " ,.-");
while (newword != 0) //create a loop that goes through the string until it gets to the end
{
if (index == size)
{
//if the string is larger than the array increase the maximum size of the array
size += 10;
//resize the array
char **words = (char **)malloc(sizeof(char *) * (size +1));
}
//asign words to its proper value
words[index] = newword;
//get the next word in the string
newword = strtok(0, " ,.-");
//increment the index to get to the next word
++index;
}
words[index] = 0;
return words;
}
break the array into the individual words then print them out th
do {
c = getchar();
words[index] = c;
} while (words[index] != '\n');
you should also add '\0' at the end of your string (after the loop) in "words" array
You are not incrementing index this way you save only the last c
you should do while(word[index] != '\0') not while(word[index] != 0 ('\0' indicates end of line no 0)
while (word[index] != 0) //loop through the list of words until the end of the list
{
printf("%s\n", word[index]); // while the words are going through the list print them out
index ++; //move on to the next word
}
I think there is a bug memory leakage because you first allocate
char **words = (char **)malloc(sizeof(char *) * (size +1)); //when declaring
when declaring the variable, and after that you again allocate the same **words in the loop body:
char **words = (char **)malloc(sizeof(char *) * (size +1)); // in the while loop
The above line in the while loop with which you allocate the space to store the string should be (1)
//in the while loop should be
char *words[index] = (char *)malloc(sizeof(char ) * (size +1));
strcpy (words[index], str);
Or simply (2)
words[index] = str;
Because the str already points to a valid memory location which you assign to the array of pointers.
In the (1) method above you are allocating a block of memory of size+1 of type char and copying the string in str into words[index] with strcpy. For this you require to reserve a memory location into words[index] first and then perform the copy. If this is the case the the memory freeing is not at simple as free (word) instead each of the allocated block will need to be manually removed.
for (index = 0; words[index] != 0; index++)
{
free (words[index];
}
free (words);
In the (2) solution is in my opinion not a good one, because you have passed a pointer to a string and assign that pointer value to store the string. So both the str and the words[index] point to the same location. Now after the function returns if anybody frees str (if it was dynamically allocated) then the words[index] reference will become illegal.
EDIT:
Also you need to use
gets (words); or in using c++ cin >> words; or use getline, or simply increment the index counter in your code, and assign a null at the end to terminate the string.
in main function. You do not increment the index counter so all the characters are assigned in the same location.
I think everybody is trying to do it the hard way.
The std streams already break the input into words using the >> operator. We just need to be more careful on how we define a word. To do this you just need to define an ctype facet that defines space correctly (for the context) and then imbue the stream with it.
#include <locale>
#include <string>
#include <sstream>
#include <iostream>
// This is my facet that will treat the ,.- as space characters and thus ignore them.
class WordSplitterFacet: public std::ctype<char>
{
public:
typedef std::ctype<char> base;
typedef base::char_type char_type;
WordSplitterFacet(std::locale const& l)
: base(table)
{
std::ctype<char> const& defaultCType = std::use_facet<std::ctype<char> >(l);
// Copy the default value from the provided locale
static char data[256];
for(int loop = 0;loop < 256;++loop) { data[loop] = loop;}
defaultCType.is(data, data+256, table);
// Modifications to default to include extra space types.
table[','] |= base::space;
table['.'] |= base::space;
table['-'] |= base::space;
}
private:
base::mask table[256];
};
Now the code looks very simple:
int main()
{
// Create the facet.
std::ctype<char>* wordSplitter(new WordSplitterFacet(std::locale()));
// Here I am using a string stream.
// But any stream can be used. Note you must imbue a stream before it is used.
// Otherwise the imbue() will silently fail.
std::stringstream teststr;
teststr.imbue(std::locale(std::locale(), wordSplitter));
// Now that it is imbued we can use it.
// If this was a file stream then you could open it here.
teststr << "This, stri,plop";
// Now use the stream normally
std::string word;
while(teststr >> word)
{
std::cout << "W(" << word << ")\n";
}
}
Testing:
> ./a.out
W(This)
W(stri)
W(plop)
With a correctly imbues stream we can use the old trick of copying from a stream into a vector:
std::copy(std::istream_iterator<std::string>(teststr),
std::istream_iterator<std::string>(),
std::back_inserter(data)
);
Lots of issues:
In your first loop you are forgetting to increment index after each read character.
Also, if you have more than 100 characters, your program will likely crash.
getchar returns an "int". Not a char. Very important - especially if you input is redirected or piped in.
Try this instead:
int tmp;
tmp = getchar();
while ((index < 99) && (tmp >= 0) && (tmp != '\n'))
{
word[index] = (char)tmp;
tmp = getchar();
index++;
}
word[index] = 0; /* make life easier - null terminate your string */
Your "findwords" function scares the hell out of me. You haven't don't have enough points on S.O. for me to elaborate on the issues here. In any case
I'm tempted to open with some lame crack about the '80s calling and wanting their obsolete "C++ as a better C" code back, but I'll try to restrain myself and just give at least some idea of how you might consider doing something like this:
std::string line;
// read a line of input from the user:
std::getline(line, std::cin);
// break it up into words:
std::istringstream buffer(line);
std::vector<std::string> words((std::istream_iterator<std::string>(buffer)),
std::istream_iterator<std::string>());
// print out the words, one per line:
std::copy(words.begin(), words.end(),
std::ostream_iterator(std::cout, "\n"));