I'm reading from file with the data below. What I need to achieve is at the end populating the vector m_hoteli with CHotel objects which have CTurist objects. 2nd-5th row are hotels with their variables and after the big numbers(500,400,300,600), that's the tourists in each hotel.
When I run it, my vectors gets filled with the information from the file but fills the details about the tourists as hotel data, thus I can't make a proper connection between the input and CTurist.
Marina 5 500 Joe 21 1 Tisho 20 6 Victoria 31 20
Tulip 4 400 Sarah 41 17 Rositsa 49 14 Valeria 24 2
BlackSea 3 300 John 35 12 Jon 35 11 Janni 28 6
SwissBell 5 600 Orlin 26 1 Margarita 27 2 Juliette 31
class CComplex:CHotel
{
protected:
string m_complex;
vector<CHotel> m_hoteli;
public:
CComplex(){};
CComplex(string filename, string nComplex)
{
string str;
m_complex = nComplex;
fstream file(filename, ios::in);
if (file.is_open())
{
CHotel temp(" ", 0, 0);
while (file >> temp)
{
m_hoteli.push_back(temp);
}
file.close();
}
else
throw "ERROR! ";
}
CHotel(String stringname) constructor:
class CHotel : public CTurist //втори клас, наследник на CTurist
{
protected:
string hName; //име хотел
int stars; //звезди на хотела
int beds; //брой легла
public:
map<CTurist, unsigned> Turisti;
unsigned Sum = 0;
int br = 0;
CHotel(){};
CHotel(string s)
{
map<CTurist, unsigned> TR;
bool first = true;
istringstream TList(s);
int i = 0;
while (getline(TList, s, ' '))
{
switch (i)
{
case 0: this->setName(s); break;
case 1: this->setAge(stoi(s)); break;
}
if (i ==2 )
{
if (!first){ setName(getName()); setAge(getAge()); first = true; }
else{ TR[CTurist("", 0)]; }
i = -1;
}
i++;
}
Turisti = TR;
}
That's some advancement I think I have made on the constructor. Looking through the breakpoints, the hotel receives it's information,aka name, stars and beds, but the tourists do not. Any tips?
CHotel(string s)
{
CTurist b("",0);
bool first = true;
istringstream TList(s);
string Days;
int i = 0;
string ime;
string godini;
if (first == true)
{
while (getline(TList, s, ' '))
{
switch (i)
{
case 0: this->hName = s; break;
case 1:this->stars = stoi(s); break;
case 2:this->beds = stoi(s); break;
}
i++;
if (i == 3)break;
}
first = false;
i = 0;
}
while (getline(TList, s, '|'))
{
switch (i)
{
case 0: ime = s; break;
case 1: godini = s; break;
case 2: Days = s; break;
}
i++;
if (i == 3)
{
CTurist T1("ime", stoi(Days));
//Turisti.insert(pair<CTurist,unsigned>("ime", stoi(Days)));
i = 0;
}
}
}
How to insert T1 into mapTuristi???
Related
My professor has asked us to make a program that will take a user's input and continue reading until the end of input. Only then, can the program output what the user has typed.
Input should be based on video title, it's url, comments made on the video, length (in minutes), and rating (in *).
For example:
United Break Guitars, https://www.youtube.com/watch?v+5YGc4zOqozo, Great example of one person getting a giant company to listen, 4.5, ***, Space Versus Tabs, https://www.youtube.com/watch?v=SsoOG6ZeyUl, Decide for yourself: spaces or tabs?, 2.83, ****
Before inputting any video description, the user needs to specify a sorting method of three choices, Rating, Length, or title. I have completed most of the code and sort method asked by my professor (bubble sort), however when I ask the program to sort by title (which is the only one of the three options that is a string), it does not output correctly.
Here is my code:
#include <iostream>
#include <stdlib.h>
#include <cstring>
using namespace std;
#include "video.h"
int main()
{
string user, url, comment, title;
int rating;
double length;
int i = 0, last = 0;
Video *videoObj[100];
// specifies how the videos should be sorted
cin >> user;
cin.ignore();
while (getline(cin,title) ) {
getline(cin, url);
getline(cin, comment);
cin >> length;
cin >> rating;
cin.ignore();
videoObj[i] = new Video(title, url, comment, length, rating);
i++;
last++;
}
//------------------------------------------------------------------------
//--------------- Sorts the list based on rating (*) ---------------------
//------------------------------------------------------------------------
if(user=="rating"){
for(int i = 0; i < last - 1; i++){
for(int j = 0; j< last - i -1; j++){
if(videoObj[j +1]->Rating(videoObj[j])){
swap(videoObj[j], videoObj[j+1]);
}
}
}
}
//------------------------------------------------------------------------
//--------------- Sorts the list based on length -------------------------
//------------------------------------------------------------------------
if(user=="length"){
for(int i = 0; i < last - 1; i++){
for(int j = 0; j< last - i -1; j++){
if(videoObj[j +1]->Length(videoObj[j])){
swap(videoObj[j], videoObj[j+1]);
}
}
}
}
//------------------------------------------------------------------------
//--------------- Sorts the list based on title --------------------------
//------------------------------------------------------------------------
if(user=="title"){
for(int i = 0; i < last - 1; i++){
for(int j = 0; j< last - i -1; j++){
if(videoObj[j +1]->Title(videoObj[j])){
swap(videoObj[j], videoObj[j+1]);
}
}
}
}
for(int i= 0; i < last; i++){
videoObj[i]->print();
}
//delete[] videoObj;
return 0;
}
video.cpp:
#include <iostream>
#include <algorithm>
using namespace std;
#include "video.h"
Video::Video(string video_title, string video_link, string video_comment, double video_length, int video_number)
: title(video_title), link(video_link), comment(video_comment), length(video_length), rating(video_number)
{
m_title = title;
m_link = link;
m_comment = comment;
m_length = length;
m_rating = rating;
}
bool Video::Rating(Video *other)
{
if(m_rating > other-> m_rating){
return true;
}
else
{
return false;
}
}
bool Video::Length(Video *other2)
{
if(m_length > other2-> m_length){
return true;
}
else
{
return false;
}
}
bool Video::Title(Video *other3)
{
if(m_length > other3-> m_length){
return true;
}
else
{
return false;
}
}
void Video::print(){
string star;
switch(rating){
case 1:
star = "*";
break;
case 2:
star = "**";
break;
case 3:
star = "***";
break;
case 4:
star = "****";
break;
case 5:
star = "*****";
break;
}
cout << title << ", " << link << ", " << comment << ", " << length << ", " << star << endl;
}
video.h:
#ifndef VIDEO_H
#define VIDEO_H
using namespace std;
class Video {
public:
Video(string video_title, string video_link, string video_comment, double video_length, int video_number);
void print();
bool Rating(Video *other);
bool Length(Video *other2);
bool Title(Video *other3);
private:
string m_title;
string m_link;
string m_comment;
double m_length;
int m_rating;
string title;
string link;
string comment;
double length;
int rating;
};
#endif
I'm not exactly sure what I need to do to title to make it function correctly. I was thinking of comparing by strings, but again, not sure where to start.
Also, another question, how do I use delete[] videoObj;without getting an error?
Well this is wrong, just a typo probably
bool Video::Title(Video *other3)
{
if(m_length > other3-> m_length){
return true;
}
else
{
return false;
}
}
It should be m_title not m_length (probably)
bool Video::Title(Video *other3)
{
if(m_title > other3-> m_title){
return true;
}
else
{
return false;
}
}
Also this code can be simplified, the above can be written in one line
bool Video::Title(Video *other3)
{
return m_title > other3-> m_title;
}
if (xxx) return true; else return false; is exactly the same as return xxx;. Beginners often don't realise you can calculate with booleans in this is way.
This is a c++ question. I want the code of the desired c++ function (see below) to be (as much as possible) in c-style and using c string library functions because I think that this will lead the quickest code in terms of execution time. (Am I wrong ? If so, how much ?) Yes, I value performance more than readability for this question because the desired function will be called a lot (millions) of times.
I am receiving const char *'s of the form "25Dec2016" that represent dates and I am parsing them to back out from them three int's (one for the day, the second for the month and the last for the year (that I assumed to be a number between 0 and 9999)) thanks to a function
Parse(const char * cDate, int & day, int & month, int & year)
I coded such a function and tested it : it works on correct const char*s (those that indeed represent date in my format), but I feel that my use of c functions (atoi for instance) is incorrect, even if I don't see why. There are also other problems :
the code is inelegant (the big if ... else if ... if) : one cannot do a switch statement on a string, but is there an elegant way to do this without resorting the std::map and c++11 ?
surely problematic from a c string point of view (I am not an expert) : for instance, I am really not happy with the way I extract the three substring into "buffers" ... Plus it appears I have problems with not null terminated char arrays that I'd like to correct. I could force a \0 at the end of _day and _year as I did for _month but I find that doing so is awful, so that I suspect a bad "design" of my parsing function
quite bad from an error handling point of view : the function is not a constructor for now, but could finally be, this is the reason why I throw.
I am open to any comments !
Here is the initial code :
Parse(const char * cDate, int & day, int & month, int & year)
{
if (0 == cDate)
{
throw "Error : null string pointer";
}
else
{
if (strlen(cDate) < 8)
{
throw "Error : invalid string format";
}
else
{
char _day[2];
char _month[4];
char _year[5]; // implictely the biggest year we authorize is 99999 ; it should suffice
for (int i = 0; i < 2; ++i)
{
_day[i] = cDate[i];
}
day = atoi(_day); // if fail, Undefined behaviour, see strtol for a more robust cross-platform alternative
char c;
for (int i = 2; i < 5; ++i)
{
c = cDate[i];
_month[i-2] = toupper(c);
}
_month[3] = '\0';
if (0 == strcmp("JAN", _month))
{
month = 1;
}
else if (0 == strcmp("FEB", _month))
{
month = 2;
}
else if (0 == strcmp("MAR", _month))
{
month = 3;
}
else if (0 == strcmp("APR",_month))
{
month = 4;
}
else if (0 == strcmp("MAY", _month))
{
month = 5;
}
else if (0 == strcmp("JUN", _month))
{
month = 6;
}
else if (0 == strcmp("JUL", _month))
{
month = 7;
}
else if (0 == strcmp("AUG", _month))
{
month = 8;
}
else if (0 == strcmp("SEP", _month))
{
month = 9;
}
else if (0 == strcmp("OCT",_month))
{
month = 10;
}
else if (0 == strcmp("NOV", _month))
{
month = 11;
}
else if (0 == strcmp("DEC", _month))
{
month = 12;
}
else
{
throw "Error : invalid month string";
}
for (int i = 5; i < 10; ++i)
{
_year[i-5] = cDate[i];
}
year = atoi(_year);
}
}
}
I finally opted for the function to be a constructor of a Date class, and inspired myself from rici's answer also using strtol as I intended initially (see comment in my initial code) instead of atoi:
#include <cstring> // for strlen
#include <ctype.h> // for toppuer
#include <stdlib.h>
int up(char c)
{
return toupper((unsigned char)(c));
}
Date::Date(const char * cDate)
{
if (0 == cDate)
{
throw "Error : null string pointer";
}
else
{
if (strlen(cDate) < 8)
{
throw "Error : invalid string format. String format is DDMMMYYYY with M's in upper or lower case"; // for now, valid format is 24Oct1979
}
else
{
char * ppEnd;
int day = strtol(cDate, &ppEnd, 10);
if (0 == day)
throw "Error : invalid string format. String format is DDMMMYYYY with M's in upper or lower case";
m_Day = day;
char cMonth[4];
int month;
memcpy(cMonth, &ppEnd[0], 3);
switch (up(cMonth[0]))
{
case 'A':
{
switch (up(cMonth[1]))
{
case 'P': if (up(cMonth[2]) == 'R') month = 4;
break;
case 'U': if (up(cMonth[2]) == 'G') month = 8;
break;
}
break;
}
case 'D':
{
if (up(cMonth[1]) == 'E' && up(cMonth[2]) == 'C')
month = 12;
break;
}
case 'F':
{
if (up(cMonth[1]) == 'E' && up(cMonth[2]) == 'B')
month = 2;
break;
}
case 'J':
{
switch (up(cMonth[1]))
{
case 'A': if (up(cMonth[2]) == 'N')
month = 1;
break;
case 'U': switch (up(cMonth[2]))
{
case 'N': month = 6;
case 'L': month = 7;
}
break;
}
break;
}
case 'M':
{
if (up(cMonth[1]) == 'A')
{
switch (up(cMonth[2]))
{
case 'R': month = 3;
case 'Y': month = 5;
}
}
break;
}
case 'N':
{
if (up(cMonth[1]) == 'O' && up(cMonth[2]) == 'V') month = 11;
break;
}
case 'O':
{
if (up(cMonth[1]) == 'C' && up(cMonth[2]) == 'T') month = 10;
break;
}
case 'S':
{
if (up(cMonth[1]) == 'E' && up(cMonth[2]) == 'P') month = 9;
break;
}
}
m_Month = (Month)month;
int year = strtol(ppEnd + 3, &ppEnd, 10);
if (0 == year)
throw "Error : invalid string format. String format is DDMMMYYYY with M's in upper or lower case";
m_Year = year;
updateSerial();
}
}
}
Remark. Being lazy, I did not throw everywhere I should in the "month" part of the code.
If your system is Posix-compatible, then you could just use strptime with the format %d%b%Y:
bool Parse(const char* date, int& day, int& month, int& year) {
struct tm components;
const char* rest = strptime(date, "%d%b%Y", &components);
if (rest == NULL || *rest != '\0') return false;
day = components.tm_mday;
month = components.tm_mon;
year = components.tm_year + 1900;
return true;
}
That is likely to be as fast as a naive parser, and it is certainly a lot easier to write :)
Otherwise, you should use strtol rather than atoi, since it will let you know both whether the parse was successful and where the next character is. And if you want to parse the month names quickly, you'll want to build a trie, either as a table or directly in code (the table is probably faster, fwiw):
static int up(char c) { return toupper((unsigned char)(c)); }
int parseMonth(const char* p) {
switch (up(p[0])) {
case 'A': {
switch (up(p[1])) {
case 'P': if (up(p[2]) == 'R') return 4;
break;
case 'U': if (up(p[2]) == 'G') return 8;
break;
}
break;
}
case 'D': {
if (up(p[1]) == 'E' && up(p[2]) == 'C') return 12;
break;
}
case 'F': {
if (up(p[1]) == 'E' && up(p[2]) == 'B') return 2;
break;
}
case 'J': {
switch (up(p[1])) {
case 'A': if (up(p[2]) == 'N') return 1;
break;
case 'U': switch (up(p[2])) {
case 'N': return 6;
case 'L': return 7;
}
break;
}
break;
}
case 'M': {
if (up(p[1]) == 'A') {
switch (up(p[2])) {
case 'R': return 3;
case 'Y': return 5;
}
}
break;
}
case 'N': {
if (up(p[1]) == 'O' && up(p[2]) == 'V') return 11;
break;
}
case 'O': {
if (up(p[1]) == 'C' && up(p[2]) == 'T') return 10;
break;
}
case 'S': {
if (up(p[1]) == 'E' && up(p[2]) == 'P') return 9;
break;
}
}
return -1;
}
Solution based on boost Spirit X3
#include <iostream>
#include <memory>
#include <string.h>
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted/std_tuple.hpp>
namespace x3 = boost::spirit::x3;
struct Month_ : x3::symbols<std::uint8_t>
{
Month_()
{
add
("Jan", 1)
("Feb", 2)
("Mar", 3)
("Apr", 4)
("May", 5)
("Jun", 6)
("Jul", 7)
("Aug", 8)
("Sep", 9)
("Oct", 10)
("Nov", 11)
("Dec", 12);
}
}month_;
using ResultType = std::tuple<int, int, int>;
namespace grammar
{
using namespace x3;
auto const start_ = rule<struct start_, ResultType>{"start"}
= int_ >> month_ >> int_;
};
int main(int athc, char* argv[])
{
std::string str{"25Dec2016"};
auto beg = std::begin(str);
auto end = std::end(str);
ResultType res;
auto ret = x3::parse(beg,end, grammar::start_, res);
if(ret && (beg==end) )
{
std::cout << "parse done :" << std::get<0>(res) << " " << std::get<1>(res) << " " << std::get<2>(res) << "\n";
}
else
{
std::cout << "parse failed '" << std::string(beg, std::next(beg, 10)) << "'\n";
}
return 0;
}
Note, this works also with char*
Live On Coliru
Here are remarks on your code and some possible simplifications:
You can remove the else branches from the error handling if tests that throw an exception. As a matter of fact, you could just return a completion status instead of throwing exceptions.
Structured exceptions would be more precise than just throwing strings, but I'm not a C++ expert.
If the input string is null-terminated after the year part, there is no need to extract the number fields, but you might want to verify correct formatting.
Using an array to match the month part would greatly simplify that part.
Here is a simpler version:
typedef enum ParseStatus {
Parse_OK = 0,
Parse_NullStringPointer,
Parse_InvalidStringFormat,
Parse_InvalidDayNumber,
Parse_InvalidMonthName,
Parse_InvalidYearNumber
} ParseStatus;
ParseStatus Parse(const char *cDate, int &day, int &month, int &year) {
static const char months[4][12] = {
"JAN", "FEB", "MAR", "APR", "MAY", "JUN",
"JUL", "AUG", "SEP", "OCT", "NOV", "DEC",
};
static const int maxday[12] = {
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
char mon[4];
unsigned int dd, mm, yyyy;
char c;
if (!cDate) {
return Parse_NullStringPointer;
}
/* Using sscanf for a simple solution.
* If the string has the correct form, `sscanf` will convert 3 fields.
* extra spaces will be accepted and ignored.
*/
if (sscanf(cDate, "%u%3s%u%c", &dd, mon, &yyyy, &c) != 3) {
return Parse_InvalidStringFormat;
}
/* If you have `strupr(char *);`, you could use it here */
mon[0] = toupper((unsigned char)mon[0];
mon[1] = toupper((unsigned char)mon[1];
mon[2] = toupper((unsigned char)mon[2];
for (mm = 0; mm < 12; mm++) {
if (!strcmp(mon, months[mm]))
break;
}
if (mm >= 12) {
return Parse_InvalidMonthName;
}
/* Further validation on the day number */
if (dd < 1 || dd > 31 || dd > maxday[mm]) {
return Parse_InvalidDayNumber;
}
/* check for leap year, assuming gregorian calendar */
if (dd == 29 && mm == 1 &&
(yyyy % 4 != 0 || (yyyy % 100 == 0 && yyyy % 400 != 0)) {
return Parse_InvalidDayNumber;
}
/* check some limits for the year */
if (yyyy < 1 || yyyy > 9999) {
return Parse_InvalidYearNumber;
}
day = dd;
month = mm + 1;
year = yyyy;
return Parse_OK;
}
If you do not want to use sscanf(), you can use strtol() this way, but it is more cumbersome:
char *p;
int i;
dd = strtol(cDate, &p, 10);
for (i = 0; i < 3 && isalpha((unsigned char)p[i]); i++) {
mon[i] = toupper((unsigned char)p[i]);
}
mon[i] = '\0';
yyyy = strtol(p, &p, 10);
if (*p != '\0') {
return Parse_InvalidStringFormat;
}
When it comes to low level parsing I think std::strtol is your friend because it keeps track of the current pointer position in the string you are parsing. Not that that is critical in this type of string with fixed length components. Also std::strtol has easy error checking.
Also using iterators and algorithms helps to keep things neat and tidy.
This is about as "elegant" as I can make this using low level constructs:
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <iostream>
void parse(const char* cDate, int& day, int& month, int& year)
{
static const char* months[] =
{
"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
};
char* pos;
day = std::strtol(cDate, &pos, 10);
if(std::distance(cDate, static_cast<const char*>(pos)) != 2)
throw std::runtime_error("bad date format");
char mon[4] = {};
for(auto m = std::begin(mon); m != std::end(mon) - 1; ++m)
*m = std::toupper(*pos++);
auto found = std::find_if(std::begin(months), std::end(months),
[&](const char* m){ return !std::strcmp(mon, m); });
if(found == std::end(months))
throw std::runtime_error("bad month format");
month = std::distance(months, found) + 1;
char* end;
year = std::strtol(pos, &end, 10);
if(std::distance(pos, end) != 4)
throw std::runtime_error("bad year format");
}
int main()
{
try
{
auto s = "25Dec2016";
int d, m, y;
parse(s, d, m, y);
std::cout << d << "/" << m << "/" << y << '\n';
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
N.B. Not heavily tested may contain bugs.
I'm not sure how to do the followings...
modify insertion() function, so that the new person will be inserted into the directory at the sorted place by the person’s name.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#pragma warning(disable: 4996)
#define max 100
typedef enum { diploma, bachelor, master, doctor } education;
const char* getDegreeName(enum education degree){
switch (degree){
case diploma: return "diploma";
break;
case bachelor: return "bachelor";
break;
case master: return "master";
break;
case doctor: return"doctor";
break;
}
}
struct person { // a node to hold personal details
char name[30];
char email[30];
int phone;
education degree;
};
struct person directory[max]; // an array of structures, 100 entries
int tail = 0; // global variable
int i = 0;
int z = 0;
char temp[30];
void flush(); // forward declaration of functions
void branching(char c);
int insertion();
int print_person(int i);
int print_all();
int search_person();
int delete_person();
int main() { // print a menu for selection
char ch = 'i';
ungetc('\n', stdin); // inject the newline character into input buffer
do {
printf("Enter your selection\n");
printf("\ti: insert a new entry\n");
printf("\td: delete an entry\n");
printf("\ts: search an entry\n");
printf("\tp: print all entries\n");
printf("\tq: quit \n");
flush(); // flush the input buffer. To be discussed later
ch = tolower(getchar());
branching(ch);
} while (ch != 113);
return 0;
}
void flush() { // flush the input buffer. To be discussed later
int c;
do {
c = getchar();
} while (c != '\n' && c != EOF);
}
void branching(char c) { // branch to different tasks
switch (c) {
case 'i':
insertion();
break;
case 's':
search_person();
break;
case 'd':
delete_person();
break;
case 'p':
print_all();
break;
case 'q':
break;
default:
printf("Invalid input\n");
}
}
int insertion() { // insert a new entry at the end
if (tail == max) {
printf("There are no more places to insert.\n");
return -1;
}
else {
printf("Enter name, phone, email:\n");
printf("Enter 0 for diploma, 1 for bachlor, 2 for master and 3 for doctor\n");
scanf("%s", directory[tail].name);
scanf("%d", &directory[tail].phone, sizeof(directory[tail].phone));
scanf("%s", directory[tail].email);
scanf("%d", &directory[tail].degree); //sacaning the degree
tail++;
printf("The number of entries = %d\n", tail);
return 0;
}
}
int print_person(int i) {
// print all information one person in the directory
printf("\n\nname = %s\n", directory[i].name);
printf("email = %s\n", directory[i].email);
printf("phone = %d\n", directory[i].phone);
printf("degree = %s\n", getDegreeName(directory[i].degree));
return 0;
}
int print_all() {
// print all information each person in the contactbook
int i;
if (tail == 0) {
printf("No entries found.");
}
else {
for (i = 0; i < tail; i++) {
print_person(i);
}
}
return 0;
}
int search_person() { // print phone and email via name
char sname[30]; int i;
printf("Please enter the name to be searched for:\n");
scanf("%s", sname); //sname is an array, no & needed
for (i = 0; i<tail; i++)
if (strcmp(sname, directory[i].name) == 0) {
print_person(i);
return i;
}
printf("The name does not exist.\n");
return -1;
}
int delete_person() {
int i, k;
k = search_person();
if (k == -1) {
printf("The name does not exist.\n"); return -1;
}
else {
for (i = k; i<tail; i++) {
strcpy(directory[i].name, directory[i + 1].name);
directory[i].phone = directory[i + 1].phone;
strcpy(directory[i].email, directory[i + 1].email);
printf("The index deleted is: %d\n", k);
}
tail--;
return k;
}
}
int insertion()
{
if (tail == max)
{
printf("There are no more places to insert.\n");
return -1;
}
return doInsertion();
}
int doInsertion() {
bool done = false;
char name[30];
char email[30];
double phone = 0;
double degree = 0;
int i = 0;
struct person toadd;
struct person tmpdirectory[tail+1];
// Ask user for input
printf("Enter name, phone, email:\n");
printf("Enter 0 for diploma, 1 for bachlor, 2 for master and 3 for doctor\n");
// Get user input
scanf("%s", name, sizeof(name));
scanf("%d", phone);
scanf("%s", sizeof(email));
scanf("%d", degree);
toadd.name = name;
toadd.phone = phone;
toadd.email = email;
toadd.degree = degree;
// Loop for over the length of the current array
for(; i < tail ; ++i)
{
// Check if the new item is alphabetically before the next item
if(strcmp(name, directory[i].name) < 0)
{
// Insert the new item and break the loop
tmpdirectory[i] = toadd;
done = true;
break;
}
// Copy over a item and keep looking for the insert spot
tmpdirectory[i] = directory[i];
}
// If we haven’t inserted yet its the last item in the list
if(done == false)
{
tmpdirectory[i] = toadd;
}
// otherwise we need to copy over the remaining items in the list
else
{
for(; i < tail ; ++i)
{
tmpdirectory[i+1]; = directory[i];
}
}
// increase list count
++tail;
// copy the struct
struct person dictionary[tail];
directory = tmpdirectory;
printf("The number of entries = %d\n", tail);
return 0;
}
Ok so my C++ knowledge is so little, i've been slowly piecing together a code but in all honesty i'm surprised i've got this far.
Just to outline my task. The user is asked to enter in several notes (musical notes, C-B including Sharps, across 9 octaves) to create a melody line and then again but a bass line. After a note has been entered, a note length must also be
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int notenumber;
struct noteStorage
{
string noteName;
int midiNumber;
int noteLength;
};
///////////////////////////////////////////////////////////////////////////////////////////////
//VALIDATION FOR NOTE NAME
bool ValidateNote(string note)
{
// Step 1: If note name length is less than 2 OR more than 3, return false
if (note.length() <2 || note.length() >3)
{
cout<<"Note length must be 2 or 3 characters\n";
return false;
}
//Step 2: If true, the note must be/(or be) between A and G
else if(tolower(note[0])<'a' || tolower(note[0]) >'g')
{
cout<<"Note must be A-G\n";
return false;
}
//Step 3: If true, the last character must be a digit
else if(isdigit(note[note.length()-1]) == false)
{
cout<<"Last character must be a digit\n";
return false;
}
//Step 4: If note length is 3 note[1] (character 2) must be '#'.
else if(note.length() == 3 && note[1] != '#')
{
"Invalid sharp note\n";
return false;
}
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////
//VALIDATION FOR NOTE LENGTH
bool ValidateNoteLength (int length)
//Step 1 - If notelength is not a digit, return FALSE
{
if (length == false)
{
cout<<"Note length must be a digit/number, please re-enter";
return false;
}
//Step 2 - If notelength is less than or equal to 0 or more than 16, return FALSE
if (length <= 0 || length > 16)
{
cout<<"Note length value cannot be less than 1 or more than 16, please re-enter";
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
int CalculateNoteNumber(string tempName)
{
int Octave;
int Note;
tempName[0] = toupper(tempName[0]);
Octave = ((tempName[tempName.length()-1]) -48) * 12;
if (tempName.length() == 2)
{
if(tempName[0] == 'C')
{
return notenumber = 0;
}
else if(tempName[0] == 'D')
{
return notenumber = 2;
}
else if(tempName[0] == 'E')
{
return notenumber = 4;
}
else if(tempName[0] == 'F')
{
return notenumber = 5;
}
else if(tempName[0] == 'G')
{
return notenumber = 7;
}
else if(tempName[0] == 'A')
{
return notenumber = 9;
}
else
{
return notenumber = 11;
}
}
else if (tempName.length() == 3)
{
if(tempName[0] == 'C')
{
return notenumber = 1;
}
else if(tempName[0] == 'D')
{
return notenumber = 3;
}
else if(tempName[0] == 'F')
{
return notenumber = 6;
}
else if(tempName[0] == 'G')
{
return notenumber = 8;
}
else
{
return notenumber = 10;
}
}
int main();
{
noteStorage noteData[8];
//string note;
for (int i = 0; i < 8; i++)
{
cout<<"Please enter note: " << i << ": ";
while (1)
{
string tempName;
cin>>tempName;
int noteNumber = CalculateNoteNumber(tempName);
if (ValidateNote(tempName) == true)
{
noteData[i].noteName = tempName;
break;
}
else
{
cout << "Please enter correctly: ";
}
} //end first while
cout<<"Please enter note length: ";
while (1)
{
int tempLength;
cin>>tempLength;
if (ValidateNoteLength(tempLength) == true)
{
noteData[i].noteLength = tempLength;
break;
}
else
{
cout << "Please enter correctly: ";
}
}//end while 2
cout<<"Thank you\n";
} //end for
cout<<"Your note and note lengths are: "<<endl;
for (int i = 0; i < 8; i++)
{
cout<<noteData[i].noteName<<"Length: ";
cout<<noteData[i].noteLength<<endl;
}
system("pause");
return 0;
}
entered (with a value in milliseconds). Once the note names and note lengths have been entered, the console then converts the notenames to the corresponding midi numbers, and outputs said midi numbers, note length and note names back to the user.
I've been having the same problem for two days now; everytime I build the solution it comes back with the same error:
"Fatal error C1075, end of file found before last brace '{' was
matched".
If anyone could point me the right way to solving this it would be much appreciated!!
You missed a } at the end of int CalculateNoteNumber(string tempName).
You will also have to remove the ; after int main() for your program to compile.
If you would have formatted your code properly you could have fixed these errors on your own.
I am trying to organize my code by grouping functions in seperate header/source files. I've #included the header file in my main .cpp, but the compiler does not see the functions in convertTypes.cpp. What gives? And how do I use my 'key' typedef globally (so also in the seperated function sources)? Lots of code, sorry.
/*
* NoteMaker.cpp
*
* Created on: Sep 4, 2013
* Author: edwinrietmeijer
*/
typedef struct {
int keyNum;
int keyType;
} key;
#include <iostream>
#include <string>
#include <iomanip>
#include "convertTypes.h"
using namespace std;
const int KEYSET[ ] = { 0, 2, 4, 5, 7, 8, 9 };
int* generateNotes( int, key );
void echoNoteList( const int* , const int, const key );
string getKeyStringFromUser();
int main() {
key keyStruct;
int octave;
int nrOfNotes;
string firstNoteName;
// Get inputs
cout << "What key would you like to generate notes in? ( f, cis, es, etc.)" << endl;
firstNoteName = getKeyStringFromUser();
cout << "In what octave would you like to generate notes? (-1 / 9)" << endl;
cin >> octave;
octave += 1;
cout << "How many notes do you wish to generate?" << endl;
cin >> nrOfNotes;
// create a key data struct from input string
keyStruct = convertKeyStringToKeyStruct( firstNoteName );
// add the starting octave nr to the keyStruct
keyStruct.keyNum += octave * 12;
// generate note list
int* noteList = new int[ nrOfNotes ];
noteList = generateNotes( nrOfNotes, keyStruct );
// echo note list to terminal
echoNoteList( noteList , nrOfNotes, keyStruct);
cin.get();
}
int* generateNotes( int notes, key keyStruct) {
int* newList = new int [notes];
int currNote = keyStruct.keyNum + keyStruct.keyType;
int currDist = 0;
newList[0] = currNote;
for (int i=1; i < notes; i ++) {
currDist = i % 7;
if ( currDist == 0 || currDist == 3 ) // half step or whole step?
{ currNote = currNote + 1; }
else
{ currNote = currNote + 2; }
newList[ i ] = currNote;
}
cout << "Generated list." << endl;
return newList;
}
void echoNoteList( const int* noteList, const int nrOfNotes, const key thisKeyStruct )
{
int currNote;
for (int i = 0; i < nrOfNotes ; i ++) {
currNote = noteList[ i ] % 12;
if ( currNote < 0 )
currNote += 12;
cout << left;
cout << setw(5) << noteList[ i ] << setw( 5 ) << convertToNoteName( currNote, thisKeyStruct.keyType ) << endl;
}
}
string getKeyStringFromUser() {
bool correctInput = false;
string getKeyName;
int keyNum;
while ( ! correctInput ) {
cin >> getKeyName;
cout << endl;
keyNum = getKeyName[ 0 ];
if ( keyNum > 96 && keyNum < 104 ) {
correctInput = true;
}
else
{
cout << "Wrong input. Try again." << endl;
}
}
return getKeyName;
}
convertTypes.h
#ifdef CONVERTTYPES_H
#define CONVERTTYPES_H
std::string convertToNoteName( int, int );
key convertKeyStringToKeyStruct( std::string );
#endif
convertTypes.cpp
/*
* convertTypes.cpp
*
* Created on: Sep 5, 2013
* Author: edwinrietmeijer
*/
#include <string>
#include "convertTypes.h"
using namespace std;
typedef struct {
int keyNum;
int keyType;
} key;
key convertKeyStringToKeyStruct( string firstNote ) {
int stringSize;
int keyType = 0;
char keyChar;
key thisKey;
keyChar = firstNote[ 0 ];
// get key type (flat, sharp, normal)
stringSize = firstNote.size( );
if (stringSize > 1 ) {
switch( firstNote[ 1 ] ) {
case 'e':
keyType = -1; break;
case 's':
keyType = -1; break;
case 'i':
keyType = 1; break;
default:
keyType = 0; break;
}
}
// convert key char to ascii code
int ASkey = keyChar;
thisKey.keyNum = KEYSET[ ASkey - 99 ];
thisKey.keyType = keyType;
return thisKey;
}
string convertToNoteName( int thisNote, int thisKeyType = 0) {
string noteName;
char addKeyType;
switch( thisKeyType ) {
case -1:
addKeyType = 'b'; break;
case 0:
addKeyType =' '; break;
case 1:
addKeyType = '#'; break;
}
switch( thisNote ) {
case 0:
noteName = "C"; break;
case 1:
if( thisKeyType == 1)
noteName = string ("C") + addKeyType;
else
noteName = string("D") + addKeyType; break;
case 2:
noteName = "D"; break;
case 3:
if( thisKeyType == 1)
noteName = string ("D") + addKeyType;
else
noteName = string("E") + addKeyType; break;
case 4:
noteName = "E"; break;
case 5:
noteName = "F"; break;
case 6:
if( thisKeyType == 1)
noteName = string ("F") + addKeyType;
else
noteName = string("G") + addKeyType; break;
case 7:
noteName = "G"; break;
case 8:
if( thisKeyType == 1)
noteName = string ("G") + addKeyType;
else
noteName = string("A") + addKeyType; break;
case 9:
noteName = "A"; break;
case 10:
if( thisKeyType == 1)
noteName = string ("A") + addKeyType;
else
noteName = string("B") + addKeyType; break;
case 11:
noteName = "B"; break;
default:
noteName = "!"; break;
}
return noteName;
}
Change:
#ifdef CONVERTTYPES_H
to:
#ifndef CONVERTTYPES_H
You are effectively compiling-out your definitions.
As to your second point, move this:
typedef struct {
int keyNum;
int keyType;
} key;
into the header file (before its first use there).
However I would warn against using a name like key as it's commonly used as a variable name. I would go for key_t or MySpecialKeyForStuffImDoing (or somesuch).
In addition to #trojanfor's anwer: also create a new NoteMaker.h containing the key structure definition or move the stuct definition to convertTypes.hso that you don't duplicate it in multiple places