Im tring to compare strings after strtok but i seem to be getting false instead of a true
is there anything else i need to do if im using strtok?
char file[] = "temp.txt";
ifstream getfile;
getfile.open(file,ios::in);
if(getfile.is_open())
{
char data[256];
char *line;
const char * test = "init";
//loop till end of file
while(!getfile.eof())
{
//get data and store to variable data
getfile.getline(data,256,'\n');
line = strtok(data," ");
while(line != NULL)
{
cout << "Comparing " << line << " with " << test <<endl;
//This is suppose to print but it dosent
if(line == test)
cout << line << endl;
line = strtok(NULL," ");
}
}
}
output :
comparing init with init
wanted output:
comparing init with init
init
thanks! :D
===========================
changed to the following and it worked! :)
if(strcmp(line,test)==0)
You're comparing pointers not content. Look into strcmp or wrap the C-strings in a std::string.
You are comparing pointers (the addresses of the strings). They will always be different. Use strcmp() to compare the strings themselves.
Related
I'm trying to invert the case of some strings, and I did it, but I have some extra characters in my return, is it a memory problem? Or because of the length?
char* invertirCase(char* str){
int size = 0;
char* iterator = str;
while (*iterator != '\0') {
size++;
iterator++;
}
char* retorno = new char[size];
for (int i = 0; i < size; i++) {
//if capital letter:
if (str[i] < 96 && str[i] > 64) {
retorno[i] = str[i] + 32;
}
// if lower case:
else if (str[i] > 96 && str[i] < 123) {
retorno[i] = str[i] - 32;
}
//if its not a letter
else {
retorno[i] = str[i];
}
}
return retorno;
}
For example, if I try to use this function with the value "Write in C" it should return "wRITE IN c", but instead it returns "wRITE IN cýýýýÝݱ7ŽÓÝ" and I don't understand where those extra characters are coming from.
PS: I know I could use a length function, but this is from school, so I can't do that in this case.
add +1 to the size of the char array.
char* retorno = new char[size+1];
add a null-terminated string before returning retorno.
retorno[size] = '\0';
Your output string is not null-terminated
When you iterate through the input string, you increment size until you reach null. That means the null is not copied to the output string. After you exit the loop, you should increment size once more to capture the end.
As an aside, it's probably a good idea to constrain size to some maximum (while(*iterator != '\0' && size < MAXSIZE)) in case someone passes a non-terminated string into your function. If you hit the max size condition, you'd need to explicitly add the null at the end of your output.
Your string should be null terminated; which is what you are looking for when you get the initial size of the string. When you create the new string, you should allocated size+1 chars of space, then retorno[size] should be set to a null terminating character (i.e. '\0'). When you attempt to print a char* using printf or cout (or similar mechanisms), it will keep printing characters until it find the null terminating character, which is why you are getting the garbage values after your expected output.
On another note, c++ has helpful functions like std::islower / std::isupper and std::tolower / std::toupper
From what I can tell, there could be 2 things going on here:
Like everyone here mentioned, the absence of a null terminating character ('\0') at the end of your char array could be causing this.
It could be the way you are printing results of your retorno character string outside of your invertirCase() function.
I tested out your function in C++14, C++17 and C++20 and it returned the correct result each time, both with the null terminating character at the end of the retorno char array and without it.
Try printing your result inside of your function before returning it, to identify if this is being caused inside of your function or outside of it. Like so:
char* invertirCase(char* str){
// [... truncated code here]
for (int i = 0; i < size; i++) {
// [... truncated code here]
}
cout << " **** TESTING INSIDE FUNCTION ****" << endl;
cout << "-- Testing index iteration" << endl;
for (int i = 0; i < size; i++) {
cout << retorno[i];
}
cout << endl;
cout << "-- Testing iterator iteration" << endl;
for (char* iterator = retorno; *iterator != '\0'; iterator++) {
cout << *iterator;
}
cout << endl;
cout << "-- Testing advanced for loop" << endl;
for (char character : retorno) {
cout << character;
}
cout << " **** END TESTING ****" << endl;
cout << endl;
return retorno;
}
This way you could possibly identify both if the problem occurs inside of your function or if the problem is occurring because of the way you may be printing your result as well.
I am trying to make a pointer to a constant character array from a c++ string.
In the last four lines, I am adding the three strings to one string. This should be used to create a pointer to a constant array. This pointer should then be returned to be used in another function. When I am debugging step by step, the "cout" at the end of the function shows the correct behaviour. When I am looking at the returned value in the main function, it points to garbage data. What am I doing wrong while returning the pointer?
const char *checkMultiID(void){
string startID = "USB0::0x2A8D::0x0101::";
string usbID = "MY54500604";
string endID = "::0::INSTR";
char answerID;
int correctFunctionInput = 0;
cout << "ID = " << usbID << "? [Y/N]" << endl;
scanf("%c", &answerID);
while(correctFunctionInput == 0){
if ((answerID == 'Y') || (answerID == 'N')){
correctFunctionInput = 1;
}
else{
cout << "Incorrect Input. Please repeat." << endl;
scanf("%c", &answerID);
}
}
if (answerID == 'N'){
cout << "Please Type in the ID like MY..." << endl;
getline (cin, usbID);
}
string fullID = startID + usbID + endID;
const char *idChar = &fullID[0];
cout << idChar << endl;
return idChar;
}
You are returning a pointer to data which is handled by a container, the c++ string, which is going out of scope, therefore is being deconstructed at the end of the function. What you want to do to get the exact behavior you describe is use a heap allocation, like so:
char* result = new char[fullID.length()+1];
std::copy(string.c_str(),string.c_str()+fullID.length()+1,result);
What you should to is return the c++ string directly, because I guarantee you that you will forget to deallocate this string eventually.
const string checkMultiID(){
return fullID;
}
You can add static keyword to fullID like :
static string fullID = startID + usbID + endID;
Better option would be to just return string.
Edit:
1201programalarm is right.
To avoid this you can do :
static string fullID;
fullID = startID + usbID + endID;
In this case after second call of checkMultiID() value from the first call will be deleted.
If you change function return type to std::string then you can just return fullID and in another function call c_str() method of returned string.
const char * result = checkMultiID().c_str();
This will solve your problem and it's the simplest solution.
I'm triying to implement my own MergeSort, but I've got some problems, see if anyone can help me a little.
I have a big file with some info separeted with coma (Name,city,mail,telf). I would like to apply mergesort to order it, because I supose that the client computer wont have as much memory to do it in one try.
So, I split it into files of MAX_CUSTOMERS lines, and order them individually, all correct until here, but when I want to get the first two files and order them, I've got all the problems, I got repeated, ones and others dissapear, here's my code:
void MergeSort(string file1Name, string file2Name,string name){
printf("Enter MERGE SORT %s AND %s\n",file1Name.c_str(),file2Name.c_str());
string temp;
string fileName;
string lineFile1, lineFile2;
bool endFil1 = false, endFil2 = false;
int numCust1 = 0;
int numCust2 = 0;
int x1 = 0, x2 = 0;
ifstream file1;
file1.open(file1Name.c_str());
ifstream file2;
file2.open(file2Name.c_str());
ofstream mergeFile;
fileName = "customers_" +name +".txt";
cout << "Result file " << fileName << endl;
mergeFile.open("temp.txt");
getline(file1,lineFile1);
getline(file2,lineFile2);
while(!endFil1 && !endFil2){
if(CompareTelf(lineFile1,lineFile2)==1){
mergeFile << lineFile1 << endl;
if(!getline(file1,lineFile1)){
cout << lineFile1 << endl;
cout << "1st file end" << endl;
endFil1 = true;
}
}else{
mergeFile << lineFile2 << endl;
if(!getline(file2,lineFile2)){
cout << lineFile2 << endl;
cout << "2nd file end" << endl;
endFil2 = true;
}
}
}
if(endFil1){
//mergeFile << lineFile2 << endl;
while(getline(file2,lineFile2)){
mergeFile << lineFile2 << endl;
}
}else{
//mergeFile << lineFile1 << endl;
while(getline(file1,lineFile1)){
mergeFile << lineFile1 << endl;
}
}
file1.close();
file2.close();
mergeFile.close();
rename("temp.txt",fileName.c_str());
return;
}
Customer SplitLine(string line){
string splitLine;
string temp;
Customer cust;
int actProp = 0;
int number;
istringstream readLineStream(line); //convert String readLine to Stream readLine
while(getline(readLineStream,splitLine,',')){
if (actProp == 0)cust.name = splitLine;
else if (actProp == 1)cust.city = splitLine;
else if (actProp == 2)cust.mail = splitLine;
else if (actProp == 3)cust.telf = atoi(splitLine.c_str());
actProp++;
}
//printf("Customer read: %s, %s, %s, %i\n",cust.name.c_str(), cust.city.c_str(), cust.mail.c_str(), cust.telf);
return cust;
}
int CompareTelf(string str1, string str2){
Customer c1 = SplitLine(str1);
Customer c2 = SplitLine(str2);
if(c1.telf<c2.telf)return 1; //return 1 if 1st string its more important than second, otherwise, return -1
else return -1;
}
struct Customer{
string name;
string city;
string mail;
long telf;
};
If have some question about the code, just say it! I tried to use varNames as descriptive as possible!
Thanks a lot.
Your code seems quite good, but it has several flaws and one important omission.
One of the minor flaws is lack of initialization of Customer structure - you didn't provide a constructor to the struct, and do no explicit initialization of the cust variable. Hopefully string members are properly initialized by the string class constructor, but long telf may get any initial value.
Another one is lack of format checking in splitting an input line. Are you sure that every input line has same format? If there are lines with too many commas (say, comma inside a name) then the loop may incorrectly try to assign 'email' data to 'telf' member...
OTOH if there is too few commas, the 'telf' member may remain uninitialized, with a random initial value...
Together with the first one this flaw may lead to incorrect order of output data.
Similar problems arise when you use atoi function: it returns int but your variable is long. I suppose you have chosen long type because of the expected range of values - if so, converting input data to int may truncate significant part of data! I'm not sure what atoi does in that case, it may either return the result of converting some initial part of the input string or just return zero. Both values are wrong and lead to incorrect sorting, so you better use atol instead.
Next issue is reading first line from both input files. You don't check if getline() succeeded. If an input file is empty, the corresponding lineFile_num string will be empty, but endFil_num will not reflect that - it will still be false. So you again go into comparing invalid data.
Finally the main problem. Assume the file1 contents is 'greater than' (that is: goes after) the whole file2. Then the first line stored in lineFile1 results in CompareTelf() returning -1 all the time. the main loop copies the whole file2 into the output, and...? And the final while() loop starts with getline(file1,lineFile1) thus discarding the first line of file1!
Similar result happens with files consisting of records (A,C) and (B), to be merged as (A,B,C): first A and B are read in, then A is saved and C is read in, then B is saved and end of file 2 detected. Then while(getline(...)) cancels C in memory and finds end of file 1, which terminates the loop. Record C gets lost.
Generally, when the main merging loop while(!endFil1 && !endFil2) exhausts one of files, the first unsaved line of the other file gets discarded. To avoid this you need to store the result of the first read:
endFil1 = ! getline(file1,lineFile1);
endFil2 = ! getline(file2,lineFile2);
then, after the main loop, start copying the input file's tail with the unsaved line:
while(!endFil1) {
mergeFile << lineFile1 << endl;
endFil1 = !getline(file1,lineFile1);
}
while(!endFil2) {
mergeFile << lineFile2 << endl;
endFil2 = !getline(file2,lineFile2);
}
I have problem in my split Sentence function .
The idea of my function split any Sentence and add it to array like
Example:
Sentence:: Hello world.
My function will works:: (array[0]= hello, array[1]= world).
This is my code
void splitSentence(char *Sentence, char symb){
const int Size = strlen(Sentence);
string SentenceResult[2];
int count= 0;
stringstream stream;
for(int i=0;i<Size;i++){
stream << Sentence[i];
if((Sentence[i] == symb) || (Sentence[i] == '\0')){
SentenceResult[count] = stream.str();
count++;
stream.str(" ");
}
}
cout << "Stream: " << stream.str() << endl;
cout << "Word [0]: " << SentenceResult[0] << endl;
cout << "Word [1]: " << SentenceResult[1] << endl;
}
The result
Stream: world
array [0]: hello
array [1]: // empty (must be "world")
What the problem in my function.
Why array[1] is empty .
const int Size = strlen(Sentence);
This calculates the length of the string data, but not the final null terminator, so your loop will not find the terminator, and won't include the last word. You want to add one to this value to get the full length of the terminated string.
Your for loop does not run far enough to get to the Sentence[i] == '\0' case. It will only run up to the "d" of "hello world", so the conent of the stream is not written anymore into the output array.
You could for example write:
const int Size = strlen(Sentence)+1;
and you have included the final null byte.
You just need to change your for loop:
for(int i=0; i <= Size; i++)
i'm writing my c++ project and in visual studio everything goes good but when i'm compiling it on ubuntu many things get wrong.
example:
int main (int argsNum, char* args[]){
Country* country = new Country("USA");
Military* military = new Military("Army",country);
Shalishut* shalishut = new Shalishut(military);
Manager* manager = Manager::GetInstance();
FileReader* fileReader = FileReader::GetInstance();
fileReader->ReadCityConfig(args,country);
fileReader->ReadRoadConfig(args,country);
fileReader->ReadMilitrayCampConfig(args,military);
military->ShowBases();
return 0;
}
void FileReader::ReadMilitrayCampConfig(char* args[], Military* military){
string line;
char inputFileName [MAX_FILE_NAME_LEN];
strcpy (inputFileName,args[3]);
ifstream myfile (inputFileName); //inputFileName
char* campName;
string cityName;
if (myfile.is_open()){
while (!myfile.eof()){ //until the end of file
getline (myfile,line); //separate each line.
if ((line.size() != 0) && (line[0] != '#')) {
campName = strtok(&line[0],",");
cityName = (string)strtok(NULL,",");
Shalishut::FixName(campName); Shalishut::FixName(&cityName[0]);
if (!(military->IsBaseExist(campName))){
if (military->GetCountry()->IsCityExist(cityName)){
Base* baseToAdd = new Base(campName,cityName);
if (baseToAdd != NULL){
military->AddBaseToMilitary(baseToAdd);
military->GetCountry()->FindCity(cityName)->AddBaseToCity(baseToAdd);
}
}
else cout << "ERROR: City named \"" << cityName << "\" does not exist, can't add base \"" << campName << "\" !" << endl<<endl;
}
else cout << "ERROR: Base Named \"" << campName << "\" is already exist in Military, can't create base!" << endl<<endl;
}
}
myfile.close();
}
else throw ExceptionMilitaryCampConfigFileFault(); /*cout << "ERROR: Unable to open MilitaryConfig file!"<< endl;*/
}
bool Country::IsCityExist(const string cityName){
map<string ,City*>::iterator itCities;
itCities = m_cities.find((string)cityName);
if (itCities != m_cities.end()) return true;
else return false;
}
void Shalishut::FixName(char* name){
int i;
name[0] = toupper(name[0]);
for (i=1 ; name[i] ; i++){
name[i] = tolower (name[i]);
}
}
}
The problem is that the program reads the cities and the roads, but when it reads the military camp i got:
" does not exist, can't add base "Hazerim" !
even though in the config file i have base in the same name.
remind: in visual studio it works perfectly!
Assuming the error message is actually ERROR: City named _____ does not exist, can't add base "Hazerim" I would look carefully at the capitalization/spelling of the cities and city-for-base in your inputs. They probably don't match.
Also using strtok on a std::string is just asking for trouble, as it's destructive and strings don't expect their internal state to be blown away randomly. There are method like find_first_of that will help you parse C++ strings.
Like others have said:
double check line endings (maybe run dos2unix on input files in lieu of a more robust / error=prone solution)
make sure the case of everything is correct, file names are case sensitive
be aware of where it is looking for files, make sure everything is in the CWD
I'd advise not messing around with std::string internals. I don't know that it's legal, and it certainly could cause problems. Use .c_str() to get the C-style string and copy it to a char [], or use string functions to parse the input.
To debug, put insome output statements so you can see what the string values are, or learn a bit about gdb and step through a short initialization run.
That cityname = (string)... is just plain ugly. Since you're not using cityname out of that scope, you can declare string cityname(...);, and cityname will always be initialized and will be defined close to where it's used.