User Input That Involves A ' ' Causes A Substring Out Of Range Error
- by Greenhouse Gases
Hi Stackoverflow people. You have already helped me quite a bit but near the end of writing this program I have somewhat of a bug. You see in order to read in city names with a space in from a text file I use a '/' that is then replaced by the program for a ' ' (and when the serializer runs the opposite happens for next time the program is run). The problem is when a user inputs a name too add, search for, or delete that contains a space, for instance 'New York' I get a Debug Assertion Error with a substring out of range expression.
I have a feeling it's to do with my correctCase function, or setElementsNull that looks at the string until it experiences a null element in the array, however ' ' is not null so I'm not sure how to fix this and I'm going a bit insane. Any help would be much appreciated. Here is my code:
// U08221.cpp : main project file.
#include "stdafx.h"
#include <_iostream>
#include <_string>
#include <_fstream>
#include <_cmath>
using namespace std;
class locationNode
{
public:
string nodeCityName;
double nodeLati;
double nodeLongi;
locationNode* Next;
locationNode(string nameOf, double lat, double lon)
{
this->nodeCityName = nameOf;
this->nodeLati = lat;
this->nodeLongi = lon;
this->Next = NULL;
}
locationNode() // NULL constructor
{
}
void swapProps(locationNode *node2)
{
locationNode place;
place.nodeCityName = this->nodeCityName;
place.nodeLati = this->nodeLati;
place.nodeLongi = this->nodeLongi;
this->nodeCityName = node2->nodeCityName;
this->nodeLati = node2->nodeLati;
this->nodeLongi = node2->nodeLongi;
node2->nodeCityName = place.nodeCityName;
node2->nodeLati = place.nodeLati;
node2->nodeLongi = place.nodeLongi;
}
void modify(string name)
{
this->nodeCityName = name;
}
void modify(double latlon, int mod)
{
switch(mod)
{
case 2:
this->nodeLati = latlon;
break;
case 3:
this->nodeLongi = latlon;
break;
}
}
void correctCase() // Correct upper and lower case letters of input
{
int MAX_SIZE = 35;
int firstLetVal = this->nodeCityName[0], letVal;
int n = 1; // variable for name index from second letter onwards
if((this->nodeCityName[0] >90) && (this->nodeCityName[0] < 123)) // First letter is lower case
{
firstLetVal = firstLetVal - 32; // Capitalise first letter
this->nodeCityName[0] = firstLetVal;
}
while(this->nodeCityName[n] != NULL)
{
if((this->nodeCityName[n] >= 65) && (this->nodeCityName[n] <= 90))
{
if(this->nodeCityName[n - 1] != 32)
{
letVal = this->nodeCityName[n] + 32;
this->nodeCityName[n] = letVal;
}
}
n++;
}
}
};
Here is the main part of the program:
// U08221.cpp : main project file.
#include "stdafx.h"
#include "Locations2.h"
#include <_iostream>
#include <_string>
#include <_fstream>
#include <_cmath>
using namespace std;
#define pi 3.14159265358979323846264338327950288
#define radius 6371
#define gig 1073741824 //size of a gigabyte in bytes
int n = 0,x, locationCount = 0, MAX_SIZE = 35 , g = 0, i = 0, modKey = 0, xx;
string cityNameInput, alter;
char targetCity[35], skipKey = ' ';
double lat1, lon1, lat2, lon2, dist, dummy, modVal, result;
bool acceptedInput = false, match = false, nodeExists = false;// note: addLocation(), set to true to enable user input as opposed to txt file
locationNode *temp, *temp2, *example, *seek, *bridge, *start_ptr = NULL;
class Menu
{
int junction;
public:
/* Convert decimal degrees to radians */
public:
void setElementsNull(char cityParam[])
{
int y=0;
while(cityParam[y] != NULL)
{
y++;
}
while(y < MAX_SIZE)
{
cityParam[y] = NULL;
y++;
}
}
void correctCase(string name) // Correct upper and lower case letters of input
{
int MAX_SIZE = 35;
int firstLetVal = name[0], letVal;
int n = 1; // variable for name index from second letter onwards
if((name[0] >90) && (name[0] < 123)) // First letter is lower case
{
firstLetVal = firstLetVal - 32; // Capitalise first letter
name[0] = firstLetVal;
}
while(name[n] != NULL)
{
if((name[n] >= 65) && (name[n] <= 90))
{
letVal = name[n] + 32;
name[n] = letVal;
}
n++;
}
for(n = 0; targetCity[n] != NULL; n++)
{
targetCity[n] = name[n];
}
}
bool nodeExistTest(char targetCity[]) // see if entry is present in the database
{
match = false;
seek = start_ptr;
int letters = 0, letters2 = 0, x = 0, y = 0;
while(targetCity[y] != NULL)
{
letters2++;
y++;
}
while(x <= locationCount) // locationCount is number of entries currently in list
{
y=0, letters = 0;
while(seek->nodeCityName[y] != NULL) // count letters in the current name
{
letters++;
y++;
}
if(letters == letters2) // same amount of letters in the name
{
y = 0;
while(y <= letters) // compare each letter against one another
{
if(targetCity[y] == seek->nodeCityName[y])
{
match = true;
y++;
}
else
{
match = false;
y = letters + 1; // no match, terminate comparison
}
}
}
if(match)
{
x = locationCount + 1; //found match so terminate loop
}
else{
if(seek->Next != NULL)
{
bridge = seek;
seek = seek->Next;
x++;
}
else
{
x = locationCount + 1; // end of list so terminate loop
}
}
}
return match;
}
double deg2rad(double deg) {
return (deg * pi / 180);
}
/* Convert radians to decimal degrees */
double rad2deg(double rad) {
return (rad * 180 / pi);
}
/* Do the calculation */
double distance(double lat1, double lon1, double lat2, double lon2, double dist) {
dist = sin(deg2rad(lat1)) * sin(deg2rad(lat2)) + cos(deg2rad(lat1)) * cos(deg2rad(lat2)) * cos(deg2rad(lon1 - lon2));
dist = acos(dist);
dist = rad2deg(dist);
dist = (radius * pi * dist) / 180;
return dist;
}
void serialise()
{
// Serialize to format that can be written to text file
fstream outfile;
outfile.open("locations.txt",ios::out);
temp = start_ptr;
do
{
for(xx = 0; temp->nodeCityName[xx] != NULL; xx++)
{
if(temp->nodeCityName[xx] == 32)
{
temp->nodeCityName[xx] = 47;
}
}
outfile << endl << temp->nodeCityName<< " ";
outfile<<temp->nodeLati<< " ";
outfile<<temp->nodeLongi;
temp = temp->Next;
}while(temp != NULL);
outfile.close();
}
void sortList() // do this
{
int changes = 1;
locationNode *node1, *node2;
while(changes > 0) // while changes are still being made to the list execute
{
node1 = start_ptr;
node2 = node1->Next;
changes = 0;
do
{
xx = 1;
if(node1->nodeCityName[0] > node2->nodeCityName[0]) //compare first letter of name with next in list
{
node1->swapProps(node2); // should come after the next in the list
changes++;
}
else if(node1->nodeCityName[0] == node2->nodeCityName[0]) // if same first letter
{
while(node1->nodeCityName[xx] == node2->nodeCityName[xx]) // check next letter of name
{
if((node1->nodeCityName[xx + 1] != NULL) && (node2->nodeCityName[xx + 1] != NULL)) // check next letter until not the same
{
xx++;
}
else break;
}
if(node1->nodeCityName[xx] > node2->nodeCityName[xx])
{
node1->swapProps(node2); // should come after the next in the list
changes++;
}
}
node1 = node2;
node2 = node2->Next; // move to next pair in list
}
while(node2 != NULL);
}
}
void initialise()
{
cout << "Populating List...";
ifstream inputFile;
inputFile.open ("locations.txt", ios::in);
char inputName[35] = " ";
double inputLati = 0, inputLongi = 0;
//temp = new locationNode(inputName, inputLati, inputLongi);
do
{
inputFile.get(inputName, 35, ' ');
inputFile >> inputLati;
inputFile >> inputLongi;
if(inputName[0] == 10 || 13) //remove linefeed from input
{
for(int i = 0; inputName[i] != NULL; i++)
{
inputName[i] = inputName[i + 1];
}
}
for(xx = 0; inputName[xx] != NULL; xx++)
{
if(inputName[xx] == 47) // if it is a '/'
{
inputName[xx] = 32; // replace it for a space
}
}
temp = new locationNode(inputName, inputLati, inputLongi);
if(start_ptr == NULL){ // if list is currently empty, start_ptr will point to this node
start_ptr = temp;
}
else
{ temp2 = start_ptr;
// We know this is not NULL - list not empty!
while (temp2->Next != NULL)
{
temp2 = temp2->Next; // Move to next link in chain until reach end of list
}
temp2->Next = temp;
}
++locationCount; // increment counter for number of records in list
}
while(!inputFile.eof());
cout << "Successful!" << endl << "List contains: " << locationCount << " entries" << endl;
inputFile.close();
cout << endl << "*******************************************************************" << endl << "DISTANCE CALCULATOR v2.0\tAuthors: Darius Hodaei, Joe Clifton" << endl;
}
void menuInput()
{
char menuChoice = ' ';
while(menuChoice != 'Q')
{
// Menu
if(skipKey != 'X') // This is set by case 'S' below if a searched term does not exist but wants to be added
{
cout << endl << "*******************************************************************" << endl;
cout << "Please enter a choice for the menu..." << endl << endl;
cout << "(P) To print out the list" << endl << "(O) To order the list alphabetically" << endl << "(A) To add a location" << endl << "(D) To delete a record" << endl << "(C) To calculate distance between two points" << endl << "(S) To search for a location in the list" << endl
<< "(M) To check memory usage" << endl << "(U) To update a record" << endl << "(Q) To quit" << endl;
cout << endl << "*******************************************************************" << endl;
cin >> menuChoice;
if(menuChoice >= 97)
{
menuChoice = menuChoice - 32; // Turn the lower case letter into an upper case letter
}
}
skipKey = ' '; //Reset skipKey so that it does not skip the menu
switch(menuChoice)
{
case 'P':
temp = start_ptr; // set temp to the start of the list
do
{ if (temp == NULL)
{
cout << "You have reached the end of the database" << endl;
}
else
{ // Display details for what temp points to at that stage
cout << "Location : " << temp->nodeCityName << endl;
cout << "Latitude : " << temp->nodeLati << endl;
cout << "Longitude : " << temp->nodeLongi << endl;
cout << endl;
// Move on to next locationNode if one exists
temp = temp->Next;
}
}
while (temp != NULL);
break;
case 'O':
{
sortList(); // pass by reference???
cout << "List reordered alphabetically" << endl;
}
break;
case 'A':
char cityName[35];
double lati, longi;
cout << endl << "Enter the name of the location: ";
cin >> cityName;
for(xx = 0; cityName[xx] != NULL; xx++)
{
if(cityName[xx] == 47) // if it is a '/'
{
cityName[xx] = 32; // replace it for a space
}
}
if(!nodeExistTest(cityName))
{
cout << endl << "Please enter the latitude value for this location: ";
cin >> lati;
cout << endl << "Please enter the longitude value for this location: ";
cin >> longi;
cout << endl;
temp = new locationNode(cityName, lati, longi);
temp->correctCase();
//start_ptr allignment
if(start_ptr == NULL){ // if list is currently empty, start_ptr will point to this node
start_ptr = temp;
}
else
{ temp2 = start_ptr;
// We know this is not NULL - list not empty!
while (temp2->Next != NULL)
{
temp2 = temp2->Next; // Move to next link in chain until reach end of list
}
temp2->Next = temp;
}
++locationCount; // increment counter for number of records in list
cout << "Location sucessfully added to the database! There are " << locationCount << " location(s) stored" << endl;
}
else
{
cout << "Node is already present in the list and so cannot be added again" << endl;
}
break;
case 'D':
{
junction = 0;
locationNode *place;
cout << "Enter the name of the city you wish to remove" << endl;
cin >> targetCity;
setElementsNull(targetCity);
correctCase(targetCity);
for(xx = 0; targetCity[xx] != NULL; xx++)
{
if(targetCity[xx] == 47)
{
targetCity[xx] = 32;
}
}
if(nodeExistTest(targetCity)) //if this node does exist
{
if(seek == start_ptr) // if it is the first in the list
{
junction = 1;
}
if(seek->Next == NULL) // if it is last in the list
{
junction = 2;
}
switch(junction) // will alter list accordingly dependant on where the searched for link is
{
case 1:
start_ptr = start_ptr->Next;
delete seek;
--locationCount;
break;
case 2:
place = seek;
seek = bridge;
seek->Next = NULL;
delete place;
--locationCount;
break;
default:
bridge->Next = seek->Next;
delete seek;
--locationCount;
break;
}
cout << endl << "Link deleted. There are now " << locationCount << " locations." << endl;
}
else
{ cout << "That entry does not currently exist" << endl << endl << endl;
}
}
break;
case 'C':
{
char city1[35], city2[35];
cout << "Enter the first city name" << endl;
cin >> city1;
setElementsNull(city1);
correctCase(targetCity);
if(nodeExistTest(city1))
{
lat1 = seek->nodeLati;
lon1 = seek->nodeLongi;
cout << "Lati = " << seek->nodeLati << endl << "Longi = " << seek->nodeLongi << endl << endl;
}
cout << "Enter the second city name" << endl;
cin >> city2;
setElementsNull(city2);
correctCase(targetCity);
if(nodeExistTest(city2))
{
lat2 = seek->nodeLati;
lon2 = seek->nodeLongi;
cout << "Lati = " << seek->nodeLati << endl << "Longi = " << seek->nodeLongi << endl << endl;
}
result = distance (lat1, lon1, lat2, lon2, dist);
cout << "The distance between these two locations is " << result << " kilometres." << endl;
}
break;
case 'S':
{
char choice;
cout << "Enter search term..." << endl;
cin >> targetCity;
setElementsNull(targetCity);
correctCase(targetCity);
if(nodeExistTest(targetCity))
{
cout << "Latitude: " << seek->nodeLati << endl << "Longitude: " << seek->nodeLongi << endl;
}
else
{
cout << "Sorry, that city is not currently present in the list." << endl << "Would you like to add this city now Y/N?" << endl;
cin >> choice;
/*while(choice != ('Y' || 'N'))
{
cout << "Please enter a valid choice..." << endl;
cin >> choice;
}*/
switch(choice)
{
case 'Y':
skipKey = 'X';
menuChoice = 'A';
break;
case 'N':
break;
default :
cout << "Invalid choice" << endl;
break;
}
}
break;
}
case 'M':
{
cout << "Locations currently stored: " << locationCount << endl <<
"Memory used for this: " << (sizeof(start_ptr) * locationCount) << " bytes" << endl << endl
<< "You can store " << ((gig - (sizeof(start_ptr) * locationCount)) / sizeof(start_ptr)) << " more locations" << endl ;
break;
}
case 'U':
{
cout << "Enter the name of the Location you would like to update: ";
cin >> targetCity;
setElementsNull(targetCity);
correctCase(targetCity);
if(nodeExistTest(targetCity))
{
cout << "Select (1) to alter City Name, (2) to alter Longitude, (3) to alter Latitude" << endl;
cin >> modKey;
switch(modKey)
{
case 1:
cout << "Enter the new name: ";
cin >> alter;
cout << endl;
seek->modify(alter);
break;
case 2:
cout << "Enter the new latitude: ";
cin >> modVal;
cout << endl;
seek->modify(modVal, modKey);
break;
case 3:
cout << "Enter the new longitude: ";
cin >> modVal;
cout << endl;
seek->modify(modVal, modKey);
break;
default:
break;
}
}
else cout << "Location not found" << endl;
break;
}
}
}
}
};
int main(array<System::String ^> ^args)
{
Menu mm;
//mm.initialise();
mm.menuInput();
mm.serialise();
}