I'm in an Intro to C++ class, and for one of our assignments we're making an online shop. One of our problems is to make a search function for the inventory, using a linear search, then display the corresponding price, stock, and shipability for that item.
For some reason, no matter how much I try and tweak it, it always returns false for the search, and that the store doesn't carry the item, even if I type in an item that I know is in the items array.
For example, if I type Moon Pie (which is in my array) in the getline, it'll still return as -1 like it isn't. Anything noticeably wrong with this code?
Here's my inputInventory.txt
Moon Pie 3.50 15 1
Cosmic Brownie 2.00 12 0
Moon Shine 7.00 7 1
Astronaut Icecream 4.00 11 1
Neptune Nuggets 2.50 30 1
Venus Vodka 6.50 10 1
Planet Pop 4.50 20 0
Starry Salad 3.00 15 0
Celeste Cakes 5.00 11 1
Plasma Potion 9.99 4 1
Star Fruit 2.50 10 1
Sun-dae 7.00 20 0
Moon Cheese 5.00 10 1
Milky Way Milkshake 6.50 5 0
Pluto Pie 7.00 9 10
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
using namespace std;
const int MAX = 15;
void searchInventory(string itemNames[], double itemCost[], int itemNoShip[MAX][2]);
int linearSearch(string arr[], int size, string value);
int main() {
int input;
string items[MAX];
double priceItems[MAX];
int noItems[MAX][2];
cout << "\n1. Read in Inventory\n";
cout << "2. Display Inventory\n";
cin >> input;
while (input > 2 || input < 1) {
cout << "An error has occured. Please input a value 1 - 2. >> ";
cin >> input;
}
switch (input) {
case 1:
if (readInventory(items, priceItems, noItems) == true) {
cout << "\nReading the file...\n";
}
break;
case 2:
searchInventory(items, priceItems, noItems);
break;
}
}
bool readInventory(string itemNames[], double itemCost[], int itemNoShip[MAX][2]) {
bool fileRead = false;
ifstream inputFile; // Pointer
inputFile.open("inputInventory.txt");
if (inputFile) // Test if file opened
{
for (int row = 0; row < MAX; row++) {
getline(inputFile, itemNames[row], '\t');
inputFile >> itemCost[row];
inputFile >> itemNoShip[row][0];
inputFile >> itemNoShip[row][1];
}
fileRead = true;
inputFile.close();
}
return fileRead;
}
void searchInventory(string itemNames[], double itemCost[], int itemNoShip[MAX][2]) {
string search;
int result;
int position;
cout << "Please type the name of the item you are looking for. > ";
cin.ignore();
getline(cin,search);
result = linearSearch(itemNames, MAX, search);
cout << result;
if (result >= 0) {
cout << "\nYour item was found!\n";
cout << itemNames[result] << itemCost[result] << itemNoShip[result][0] << "Shippable:" << itemNoShip[result][1];
}
else {
cout << "\nThis item was not found in the list.";
}
}
int linearSearch(string arr[], int size, string value) {
int position;
int index;
for (index = 0; index < size; index++) {
if (arr[index] == value) {
position = index;
}
else {
position = -1;
}
}
return position;
}
for (index = 0; index < size; index++)
if (arr[index] == value) {
position = index;
}
else {
position = -1;
}
This loop continually overwrites position.
Unless your sought-after element is the last one in the array, immediately after it's been found the next element will cause position to be set to -1 again (unless that one matches too 😋).
You should stop looping (or, at least, stop updating position) as soon as a match is found.
Also, it would be advisable to wrap the entire loop body in {} braces, as that is conventional and what people will expect to see, making the code easier to read and to understand.
How about:
int linearSearch(string arr[], int size, string value)
{
for (int index = 0; index < size; index++)
{
if (arr[index] == value)
return index;
}
return -1;
}
You should break out of the for loop once you found the item, if not the loop will just continue and overwrite position. Edited: Based on PaulMcKenzie's comment, you should initialize position with a value so that it will not return garbage value.
int linearSearch(string arr[], int size, string value) {
int position = -1;
int index;
for (index = 0; index < size; index++) {
if (arr[index] == value) {
position = index;
break;
}
}
return position;
}
The problem is cin.ignore(), since the default value of the first parameter is one, the first letter will always be taken out. So if someone typed "Moon Pie", the value in search will be "oon Pie".
I creating a project from college where I have to recreate the Scrabble Junior game to a console Game, but I've got a problem and i question in my code.
Firstly, I've got an error in my code saying :
"no instance of overloaded function "std::vector<_Ty, _Alloc>::push_back [with _Ty=Board::Word, _Alloc=std::allocator<Board::Word>]" matches the argument list
argument types are: (Board::Word)
object type is: std::vector<Board::Word, std::allocator<Board::Word>>
The struct Word is this one:
struct Word {
int row;
int column;
char orientation;
int tilesadded = 0; //starts at 0
int wordlength;
bool completed = false;
int currentletterpositiontoAdd[2]; //array to hold the coordenates of the next tile to be added
std::string name;
};
This struct basically stores every word and it's position in the board
And then I have also a vector storing every word struct: std::vector <Word> words;
The code that builds this struct is the following (because I get from a file every word and position to the board):
void Board::GetBoard()
{
std::ifstream file;
std::string filename, input;
std::cout << "----------------------------------------------------------------------------------------" << std::endl << std::endl;
std::cout << "What is the directory of the board file? (.txt is added for you) -> ";
while (std::getline(std::cin, filename))
{
file.open(filename + ".txt");
if (!file.is_open())
{
std::cin.clear();
file.clear();
RED;
std::cerr << "Error reading file." << std::endl;
WHITE;
std::cout << "What is the directory of the board file? (.txt is added for you) -> ";
}
else
break;
}
while (std::getline(file, input))
{
if ((int)input[0] == 49 || (int)input[0] == 50)
{ //this means that its the first line of the file and the first character is either 1 or 2
boardSize = stoi(input.substr(0, 2));
}
else
{
std::string nametoCat;
Word word;
word.row = input[0] - 'A' + 1; //calculation of the position on the board using ascii code ex: input[0] = C so: 'C' -'A' + 1 = 3 row -> 3
word.column = input[1] - 'a' + 1; //calculation of the position on the board using ascii code ex: input[1] = e so: 'e' -'a' + 1 = 5 column -> 5
word.orientation = input[3];
word.currentletterpositiontoAdd[0] = word.row;
word.currentletterpositiontoAdd[1] = word.column;
for (int x = 5; x < 1000000000; x++)
{ //for loop to check the name ending and build a string with the name
if (input[x] == '\0')
break;
else
nametoCat += input[x];
}
word.name = nametoCat;
word.wordlength = word.name.size(); //storing the word length to use later to check if word is completed in board
words.push_back(word);
}
}
}
The file looks like this:
15 x 15
Ak H EGGS
Bg H BUZZ
Ca H MUSIC
Cm H ARM
...
And secondly, I would like to make the code look more "clean" and understandable and remove that 1000000000 from: for (int x = 5; x < 1000000000; x++) and do it another way, but i can't find a solution. Because this 1000000000 looks like a magic number and not a number that would always work, for example, wouldn't work with a word of size 1000000001 (unlikely but possible).
Thank you.
I have got some doubts while solving - Name That Number.
It goes like this -
Among the large Wisconsin cattle ranchers, it is customary to brand cows with serial numbers to please the Accounting Department. The cowhands don't appreciate the advantage of this filing system, though, and wish to call the members of their herd by a pleasing name rather than saying, "C'mon, #4734, get along."
Help the poor cowhands out by writing a program that will translate the brand serial number of a cow into possible names uniquely associated with that serial number. Since the cowhands all have cellular saddle phones these days, use the standard Touch-Tone(R) telephone keypad mapping to get from numbers to letters (except for "Q" and "Z"):
2: A,B,C 5: J,K,L 8: T,U,V
3: D,E,F 6: M,N,O 9: W,X,Y
4: G,H,I 7: P,R,S
Acceptable names for cattle are provided to you in a file named "dict.txt", which contains a list of fewer than 5,000 acceptable cattle names (all letters capitalized). Take a cow's brand number and report which of all the possible words to which that number maps are in the given dictionary which is supplied as dict.txt in the grading environment (and is sorted into ascending order).
For instance, brand number 4734 produces all the following names:
GPDG GPDH GPDI GPEG GPEH GPEI GPFG GPFH GPFI GRDG GRDH GRDI
GREG GREH GREI GRFG GRFH GRFI GSDG GSDH GSDI GSEG GSEH GSEI
GSFG GSFH GSFI HPDG HPDH HPDI HPEG HPEH HPEI HPFG HPFH HPFI
HRDG HRDH HRDI HREG HREH HREI HRFG HRFH HRFI HSDG HSDH HSDI
HSEG HSEH HSEI HSFG HSFH HSFI IPDG IPDH IPDI IPEG IPEH IPEI
IPFG IPFH IPFI IRDG IRDH IRDI IREG IREH IREI IRFG IRFH IRFI
ISDG ISDH ISDI ISEG ISEH ISEI ISFG ISFH ISFI
As it happens, the only one of these 81 names that is in the list of valid names is "GREG".
Write a program that is given the brand number of a cow and prints all the valid names that can be generated from that brand number or ``NONE'' if there are no valid names. Serial numbers can be as many as a dozen digits long.
Here is what I tried to solve this problem. Just go through all the names in the list and check which is satisfying the constraints given.
int numForChar(char c){
if (c=='A'||c=='B'||c=='C') return 2;
else if(c=='D'||c=='E'||c=='F') return 3;
else if(c=='G'||c=='H'||c=='I') return 4;
else if(c=='J'||c=='K'||c=='L') return 5;
else if(c=='M'||c=='N'||c=='O') return 6;
else if(c=='P'||c=='R'||c=='S') return 7;
else if(c=='T'||c=='U'||c=='V') return 8;
else if(c=='W'||c=='X'||c=='Y') return 9;
else return 0;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
freopen("namenum.in","r",stdin);
freopen("namenum.out","w",stdout);
string S; cin >> S;
int len = S.length();
freopen("dict.txt","r",stdin);
string x;
while(cin >> x){
string currName = x;
if(currName.length() != S.length()) continue;
string newString = x;
for(int i=0;i<len;i++){
//now encode the name as a number according to the rules
int num = numForChar(currName[i]);
currName[i] = (char)num;
}
if(currName == S){
cout << newString << "\n";
}
}
return 0;
}
Unfortunately, when I submit it to the judge, for some reason, it says no output produced that is my program created an empty output file. What's possibly going wrong?
Any help would be much appreciated. Thank You.
UPDATE: I tried what Some Programmer Dude suggested by adding a statement else return 0; at the end of the numOfChar function in case of a different alphabet. Unfortunately, it didn't work.
So after looking further at the question and exploring the information for Name That Number. I realized that it is not a current contest, and just a practice challenge. Thus, I updated my answer and also giving you my version of a successful submission. Nonetheless, that is a spoiler and will be posted after why your code was not working.
First, you forgot a } after the declaration of your number function. Secondary, you did not implement anything to check whether if the input fail to yield a valid name. Third, when you use numForChar() on the character of currName, the function yielded an integer value. That is not a problem, the problem is that it is not the ASCII code but is a raw number. You then compare that against a character of the input string. Of which, is an ASCII's value of a digit. Thus, your code can't never find a match. To fix that you can just add 48 to the return value of the numForChar() function or xor the numForChar() return's value to 48.
You are on the right track with your method. But there is a few hints. If you are bored you can always skip to the spoiler. You don't need to use the numForChar() function to actually get a digit value from a character. You can just use a constant array. A constant array is faster than that many if loop.
For example, you know that A, B, C will yield two and A's ASCII code is 65, B's is 66, and C's equal to 67. For that 3, you can have an array of 3 indexes, 0, 1, 2 and all of them stores a 2. Thus, if you get B, you subtract B's ASCII code 65 will yield 1. That that is the index to get the value from.
For getting a number to a character you can have a matrix array of char instead. Skip the first 2 index, 0 and 1. Each first level index, contain 3 arrays of 3 characters that are appropriate to their position.
For dictionary comparing, it is right that we don't need to actually look at the word if the length are unequal. However, besides that, since their dictionary words are sorted, if the word's first letter is lower than the range of the input first letter, we can skip that. On the other hand, if words' first letter are now higher than the highest of the input first letter, there isn't a point in continue searching. Take note that my English for code commenting are almost always bad unless I extensively document it.
Your Code(fixed):
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int numForChar(char c){
if (c=='A'||c=='B'||c=='C') return 2;
else if(c=='D'||c=='E'||c=='F') return 3;
else if(c=='G'||c=='H'||c=='I') return 4;
else if(c=='J'||c=='K'||c=='L') return 5;
else if(c=='M'||c=='N'||c=='O') return 6;
else if(c=='P'||c=='R'||c=='S') return 7;
else if(c=='T'||c=='U'||c=='V') return 8;
else if(c=='W'||c=='X'||c=='Y') return 9;
else return 0;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
ifstream fin("namenum.in");
ifstream dict("dict.txt");
ofstream fout("namenum.out");
string S;
fin >> S;
int len = S.length();
bool match = false;
string x;
while(dict >> x){
string currName = x;
if(currName.length() != S.length()) continue;
string newString = x;
for(int i=0;i<len;i++){
//now encode the name as a number according to the rules
int num = numForChar(currName[i]) ^ 48;
currName[i] = (char)num;
}
if(currName == S){
fout << newString << "\n";
match = true;
}
}
if ( match == false ){
fout << "NONE" << endl;
}
return 0;
}
Spoiler Code(Improved):
#include <fstream>
#include <string>
using namespace std;
// A = 65
// 65 - 0 = 65
const char wToN[] = {
// A ,B ,C ,D ,E ,F ,G ,H ,I ,
'2','2','2','3','3','3','4','4','4',
// J ,K ,L ,M ,N ,O ,P ,Q ,R ,S
'5','5','5','6','6','6','7','7','7','7',
// T ,U ,V ,W ,X ,Y ,Z
'8','8','8','9','9','9','9'
};
// 2 = {A, B, C} = 2[0] = A, 2[1] = B, 2[2] C
const char nToW[10][3] = {
{}, // 0 skip
{}, // 1
{'A','B','C'},
{'D','E','F'},
{'G','H','I'},
{'J','K','L'},
{'M','N','O'},
{'P','R','S'},
{'T','U','V'},
{'W','X','Y'}
};
int main(){
ifstream fin("namenum.in");
ifstream dict("dict.txt");
ofstream fout("namenum.out");
string S;
fin >> S;
// Since this will not change
// make this a const to make it
// run faster.
const int len = S.length();
// lastlen is last Index of length
// We calculate this value here,
// So we do not have to calculate
// it for every loop.
const int lastLen = len - 1;
int i = 0;
unsigned char digits[len];
unsigned char firstLetter[3];
// If not match print None
bool match = false;
for ( ; i < len; i++ ){
// No need to check upper bound
// constrain did not call for check.
if ( S[i] < '2' ) {
fout << "NONE" << endl;
return 0;
}
}
const char digit1 = S[0] ^ 48;
// There are 3 set of first letter.
// We get them by converting digits[0]'s
// value using the nToW array.
firstLetter[0] = nToW[digit1][0];
firstLetter[1] = nToW[digit1][1];
firstLetter[2] = nToW[digit1][2];
string dictStr;
while(dict >> dictStr){
// For some reason, when keeping the i = 0 here
// it seem to work faster. That could be because of compiler xor.
i = 0;
// If it is higher than our range
// then there is no point contineuing.
if ( dictStr[0] > firstLetter[2] ) break;
// Skip if first character is lower
// than our range. or If they are not equal in length
if ( dictStr[0] < firstLetter[0] || dictStr.length() != len ) continue;
// If we are in the letter range
// we always check the second letter
// not the first, since we skip the first
i = 1;
for ( int j = 1; j < len; j++ ){
// We convert each letter in the word
// to the corresponding int value
// by subtracting the word ASCII value
// to 65 and use it again our wToN array.
// if it does not match the digits at
// this current position we end the loop.
if ( wToN[dictStr[i] - 65] != S[j] ) break;
// if we get here and there isn't an unmatch then it is a match.
if ( j == lastLen ) {
match = true;
fout << dictStr << endl;
break;
}
i++;
}
}
// No match print none.
if ( match == false ){
fout << "NONE" << endl;
}
return 0;
}
I suggest you use c++ file handling. Overwriting stdin and stdout doesn't seem appropriate.
Add these,
std::ifstream dict ("dict.txt");
std::ofstream fout ("namenum.out");
std::ifstream fin ("namenum.in");
Accordingly change,
cin >> S --to--> fin >> S;
cin >> x --to--> dict >> x
cout << newString --to--> fout << newString
I am having trouble debugging this code so it reads two columns from the file and when the first column (Department is the same it just adds the second column to the old dept already created)This code is having trouble with looping. Any walk through, help would be much appreciated ! Thanks.
#include <iostream>
#include <fstream>
using namespace std;
ifstream inputFile; //stream object
int main()
{
inputFile.open("text.txt");
const int SIZE = 15;
int candy[SIZE];
int dept[SIZE];
int valuecounter = 0;
int Candy;
int Department;
while (inputFile >> Department >> Candy)
{
// Exit loop if we have filled the entire array
if (valuecounter == SIZE)
break;
// Update previous values
for (int index = 0; index < valuecounter; index++)
{
if (dept[index] == Department)
{
candy[index] += Candy;
}
}
// Update current values and increment counter
dept[valuecounter] = Department;
candy[valuecounter] = Candy;
valuecounter++;
}
for (int i = 0; i < valuecounter ; i++)
cout << dept[i] << " " << candy[i] << endl;
inputFile.close();
return 0;
}
and the list of input being for ex:
910 8
450 9
750 10
150 35
750 19
150 18
910 19
390 19
520 6
110 78
300 23
110 1
110 5
120 6
150 16
300 23
110 1
110 5
120 6
150 16
the array should be partially filled. but it produces weird outcome! logic error?
You have initialized valuecounter, and all elements of array dept, to zero. For this reason, in every iteration of the while loop, condition dept[valuecounter] == NULL will always evaluate to true. This means that, in every iteration, only code in the first if statement will execute.
Note that this is not the only problem with this code. As user Crazy Eddie pointed out, using NULL as an integer is considered very bad practice.
EDIT:
Replace your while loop with the following:
while (inputFile >> Department >> Candy)
{
// If this Department already exists ...
for (int index = 0; index < valuecounter; index++)
{
if (dept[index] == Department)
{
// ... update the corresponding value in 'candy' and continue the loop
candy[index] += Candy;
continue;
}
}
// If this Department does not exist, and if there are are more
// available array elements, assign a new Department
if (valuecounter < SIZE)
{
dept[valuecounter] = Department;
candy[valuecounter] = Candy;
valuecounter++;
}
}
int main()
{
const int SIZE = 15;
int candy[SIZE];
int dept[SIZE];
int valuecounter = 0;
int Candy;
int Department;
inputFile.open("text.txt");
if (!inputFile)
{
cout << "\nError opening file" << endl
<< "Exiting\n" << endl;
exit(1);
}
while (valuecounter < SIZE && inputFile >> Department >> Candy)
{
//setting bool to false everytime loop starts over.
bool found = false;
for (int index = 0; index < valuecounter; index++)
{
if (dept[index] == Department)
{
candy[index] += Candy;
//setting bool to true if department found
found = true;
}
}
if (!found)
{
dept[valuecounter] = Department;
candy[valuecounter] = Candy;
valuecounter++;
}
}
inputFile.close();
if (valuecounter == 0)
{
cout << "\n\nEMPTY FILE\nExiting.\n\n" << endl;
exit(1);
}
here i had three different functions do different things with data
return 0;
}
basically what i was missing is to set bool back to false whenever while loop started over this is why data wasn't calculated properly. Anyways Thank you all for help and all the tips!
I'm trying to read values from a text file and then stick them into an array. The text file I'm using is like this;
Big Stone Gap,VA
Red-tailed_Hawk 1
Mourning_Dove 66
Red-bellied_Woodpecker 5
Yellow-bellied_Sapsucker 1
Downy_Woodpecker 4
Pileated_Woodpecker 2
Blue_Jay 8
American_Crow 89
Carolina_Chickadee 8
Black-capped_Chickadee 6
Tufted_Titmouse 12
Red-breasted_Nuthatch 2
White-breasted_Nuthatch 9
Carolina_Wren 3
American_Robin 1
Northern_Mockingbird 1
European_Starling 5
Eastern_Towhee 3
Field_Sparrow 13
Fox_Sparrow 1
Song_Sparrow 8
White-throated_Sparrow 11
Dark-eyed_Junco 9
Northern_Cardinal 30
Purple_Finch 7
House_Finch 6
American_Goldfinch 29
I'm trying to read them into an array, and then output the array. I have to do other stuff, (alter the values from user input), but I know how to do that. My problem is this; When I run it, it reads Big and Stone, and puts them into the values. This isn't what I want; It should be something like Red_Tailed_Hawk and 1. I have my code below; I have no idea what to do to get it to work right. Please excuse some of the variables; I cannibalized this script from another project I did with int arrays instead of string arrays.
#include <iostream>
#include <fstream>
#include <iomanip>
using namespace std;
const int NUMBER_OF_STUDENTS = 28;
const int NUMBER_OF_SCORES = 28;
void getData (ifstream& infile, string matrix[][NUMBER_OF_SCORES + 1 ],
int NUMBER_OF_STUDENTS) ;
void printMatrix(string matrix[][NUMBER_OF_SCORES + 1], int NUMBER_OF_STUDENTS);
int main()
{
string birdarray [NUMBER_OF_STUDENTS][NUMBER_OF_SCORES +1 ] ;
// column 0 will hold the student ID.
// row n has the ID and birdarray for student n.
ifstream inData; //input file stream variable
inData.open("one.txt");
if ( !inData)
{
cout << "invalid file name \n";
return 1;
}
// input the birdarray into two-D array birdarray
getData ( inData , birdarray, NUMBER_OF_STUDENTS );
printMatrix(birdarray, NUMBER_OF_STUDENTS);
// return the row number of a searchItem in a particular column.
// if not found, return -1
}
void getData (ifstream& infile,string chart[][NUMBER_OF_SCORES + 1 ],
int student_count)
{
int row, col;
for ( row = 0; row < student_count; row++)
for (col =0; col < NUMBER_OF_SCORES +1 ; col++)
infile >> chart [row] [col] ;
}
void printMatrix(string matrix[][NUMBER_OF_SCORES + 1], int NUMBER_OF_STUDENTS)
{
int row, col;
//this code below is to loop it so it displays all the values
// for (row = 0; row < NUMBER_OF_STUDENTS + 1; col++)
// {
// cout << right << matrix[row][col];
// cout << endl;
//}
//this is just some test code to see if it can output certain values right
cout << matrix[0][2];
cout << matrix[0][3];
cout << matrix[0][4];
cout << matrix[0][5];
}
You can skip the first line with the following:
inData.ignore(numeric_limits<streamsize>::max(), inData.widen('\n'));
std::istream::ignore reference: http://www.cplusplus.com/reference/istream/istream/ignore/
PS. The first parameter is a maximum number of chars to ignore. numeric_limits::max() means that you want to ignore until the string ends, no matter how long the string is.
There's an answer here:
How do I read a text file from the second line using fstream?
Just read the first line before doing the getData call.
string line;
getline(infile, line)
for ( row = 0; row < row_count; row++)
for (col =0; col < column_count +1 ; col++)
getline(infile, line);
chart[row][col] = line;
Streams behave like their namesake. You can't skip what comes in the stream like jumping through an array, all you can do is ignore stuff as it floats by.
You can use the seekg() function which moves the FPM (i.e. File Position Marker) any amount of space you want. That is probably the easiest way to do it.
From you code it would look something like
inData.seekg(16, std::ios::beg) //Seeks past 16 character in the file