Working with structs? - c++

I'm having trouble working with strings (char*) in structs. I can't seem to call the right data I want too.
Under processFile it shows the struct member correctly; under main it does not.
Here is my code:
#include <iostream>
#include <io.h>
#include <string>
#include "dirent.h"
#include "Stream.h"
#include "Compression.h"
#include "Definitions.h"
using namespace std;
using namespace System::IO;
using namespace System::Funcs;
bool isRawfile(char* ext);
void ProcessDirectory(string directory);
void ProcessFile(char* file);
void ProcessEntity(struct dirent* entity);
typedef struct
{
char *name;
int usize;
int csize;
BYTE *data;
} rawfile;
string path = "";
string source;
int numrawfiles = 0, numstringtables = 0;
rawfile *rawfiles = new rawfile[0x400];
FILE * zone = fopen( "C:\\Users\\jake\\Desktop\\patch_mp.zone" , "wb" );
int main(int argc, char **args)
{
if(args[1] != NULL)
{
source = string(args[1]) + "\\"; //maybe move under else here..
if(strchr(args[1], '.') != NULL)
{
cout<<"Unable to compile files, please drag a folder to compile."<<endl;
cin.get();
return 0;
}
else
{
int header[] = {1,0,0x3B4,0,0,0,1,0,0x1000,0,0,0,-1};
for(int i=0; i<13; i++)
fwrite(Converter::Int32ToBytes(header[i]), 1 , 4 , zone );
ProcessDirectory(args[1]);
for(int i=0; i<numrawfiles; i++)
cout<<"Name: "<<rawfiles[i].name<<" Length: "<< rawfiles[i].usize << " - in main()"<<endl;
fclose(zone);
}
}
else
{
cout<<"No folder selected to compile. Press any Key to quit."<<endl;
cin.get();
return 0;
}
cin.get();
return 0;
}
void ProcessDirectory(string directory)
{
string dirToOpen = path + directory;
auto dir = opendir(dirToOpen.c_str());
path = dirToOpen + "\\";
if(NULL == dir)
{
cout << "could not open directory: " << dirToOpen.c_str() << endl;
return;
}
auto entity = readdir(dir);
while(entity != NULL)
{
ProcessEntity(entity);
entity = readdir(dir);
}
path.resize(path.length() - 1 - directory.length());
closedir(dir);
}
void ProcessEntity(struct dirent* entity)
{
if(entity->d_type == DT_DIR)
{
if(entity->d_name[0] == '.')
return;
ProcessDirectory(string(entity->d_name));
return;
}
if(entity->d_type == DT_REG)
{
string fullpath = path + entity->d_name;
ProcessFile(const_cast<char *>(fullpath.c_str()));
return;
}
cout << "Not a file or directory: " << entity->d_name << endl;
}
void ProcessFile(char* file)
{
char* extension = strrchr(file, '.');
if(isRawfile(extension))
{
rawfile raw;
raw.name = (char *)&file[source.length()];
raw.usize = File::getFileSize(file);
rawfiles[numrawfiles] = raw;
cout<<"Name: "<<rawfiles[numrawfiles].name<<" Length: "<< raw.usize << " - in ProcessFile()"<<endl;
fwrite(Converter::Int32ToBytes(0x23),1,4,zone);
fwrite(Converter::Int32ToBytes(-1),1,4,zone);
numrawfiles++;
}
}
bool isRawfile(char* ext)
{
char *exts[11] = {".gsc",".cfg",".txt",".news",".png",".vision",".rmb",".script",".arena",".atr",".csc"};
for(int i=0; i<10; i++)
if(strncmp(ext,exts[i],strlen(exts[i]))==0)
return true;
return false;
}
Here is an example picture:
What am I doing wrong?

Save yourself a lot of trouble by using an std::string:
#include <string>
struct thing
{
std::string name;
int age;
};
You can also avoid the dynamically allocated array:
#include <vector>
std::vector<thing> things(3);

You are outputting the memory address of "Ben" instead of the actual String. You should use
cout << things[1]->name << endl;
which is syntactic sugar for
cout << (*things[1]).name << endl;

To add to what scd said, you are not successfully dereferencing the name member. When you try and operate on things[1].name you are operating on a pointer, which is simply a memory location.
This is one of the trickiest things when learning to use pointers. Here is some more reading on the dereference operator, and syntax hints.
http://en.wikipedia.org/wiki/Dereference_operator#Other_syntax
Edit:
After compiling myself, I realized that I was on the wrong track with this one, and that std::cout will correctly handle the char pointer. You should be able to solve this with your code, just ensure that you give your struct array a size.
This worked for me:
#include <iostream>
#define MAXSIZE 3
typedef struct
{
char* name;
int age;
}Thing;
Thing *things = new Thing[MAXSIZE];
int _tmain(int argc, _TCHAR* argv[])
{
char* names[MAXSIZE] = { "Alice", "Ben", "Carlos" };
int ages[MAXSIZE] = { 24, 25, 26 };
for(int i=0; i<MAXSIZE; i++)
{
things[i].name = names[i];
things[i].age = ages[i];
}
std::cout << things[1].name << std::endl;
return 0;
}

Related

Hash multiple files

I'm trying to hash multiple files, but there is an error.
My files name start from Cheque 083654.tif - 08365122.tif
My code:
for (int i = 4; i < 123; i++)
{
stringstream file;
file<< "C:/Users/user/Desktop/datasets/Cheque 08365" << i << ".tif";
string filename = file.str();
cout << filename << '\n';
unsigned char *sha256digest = calculateSHA256(filename);
char *sha256hash = (char *)malloc(sizeof(char) * 65);
sha256hash[65] = '\0';
for (int i = 0; i < SHA256_DIGEST_LENGTH; i++)
{
sprintf(&sha256hash[i * 2], "%02x", sha256digest[i]);
}
printf("SHA256 HASH: %s\n", sha256hash);
system("pause");
}
The error states that no suitable conversion function from string to char * exists at the filename in:
unsigned char *sha256digest = calculateSHA256(filename);
How can I solve this error?
If calculateSHA256 returns std::string, that assignment is illegal in more ways than one. char * is just a pointer to that storage, string returned by function is a temporal object, which stops existing after semicolon. First, you have to save that string, second, to access its data by appropriate member function. There is no way to convert string directly to a pointer.
Or don't use pointer at all. You would find it better to avoid using C idioms at all.
std::string sha256digest = calculateSHA256(filename);
// FORMATTED OUTPUT
std::stringstream hashstr;
hashstr << std::hex << std::setfill('0');
for( auto x : sha256digest ) // this would iterate through entirety of string
{
hashstr << std::setw(2) << static_cast<int>(static_cast<unsigned char>(x));
}
std::string output;
hashstr >> output;
std::cout << "SHA256 HASH: " << output;
You do not need a stringstream to construct the filename. Use std::to_sting().
I think most of the simplification can be done inside your own calculateSHA256() function. Let it return a std::vector or std::string instead of a char*.
Here's an example where I let it return a std::vector<std::uint8_t> instead:
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <cstdint>
#include <cstdio>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <stdexcept>
#include <string>
#include <vector>
// An EVP_MD_CTX helper class
class EvpMdCtx {
public:
explicit EvpMdCtx(const EVP_MD* type, ENGINE* impl = nullptr) : EvpMdCtx() {
if(init(type, impl) == 0)
throw std::runtime_error("EVP_DigestInit_ex failed");
}
EvpMdCtx() : ctx(EVP_MD_CTX_new()) {
if(ctx == nullptr) throw std::runtime_error("EVP_MD_CTX_new failed");
}
EvpMdCtx(const EvpMdCtx&) = delete;
EvpMdCtx& operator=(const EvpMdCtx&) = delete;
~EvpMdCtx() { EVP_MD_CTX_free(ctx); }
int init(const EVP_MD* type, ENGINE* impl = nullptr) {
return EVP_DigestInit_ex(ctx, type, impl);
}
int update(const void* d, size_t cnt) { return EVP_DigestUpdate(ctx, d, cnt); }
auto finalize() {
std::vector<std::uint8_t> md_value(EVP_MAX_MD_SIZE);
unsigned md_len;
if(EVP_DigestFinal_ex(ctx, md_value.data(), &md_len) == 0)
md_value.clear();
else
md_value.resize(md_len);
return md_value;
}
private:
EVP_MD_CTX* ctx;
};
std::vector<std::uint8_t> calculateSHA256(const std::string& filename) {
std::ifstream is(filename);
if(not is) return {};
EvpMdCtx ctx(EVP_sha256());
char buf[BUFSIZ]; // a buffer to fill
while(true) {
is.read(buf, std::size(buf));
auto len = is.gcount();
if(len > 0) {
if(ctx.update(buf, static_cast<size_t>(len)) == 0) return {};
} else {
break;
}
}
// finalize
return ctx.finalize();
}
int main() {
const std::string file = "C:/Users/user/Desktop/datasets/Cheque 08365";
for(int i = 4; i <= 122; ++i) {
std::string filename = file + std::to_string(i) + ".tif";
auto res = calculateSHA256(filename);
if(res.empty()) {
std::cout << "Failed: " << filename << '\n';
} else {
std::cout << std::hex << std::setfill('0');
for(auto v : res) {
std::cout << std::setw(2) << static_cast<int>(v);
}
std::cout << ' ' << filename << '\n';
}
}
}

How to make sure that two strings only have certain alphabets in c++

Aim is to make sure that the user entered input for string 1 and string 2 contains only characters A,T,G or C in any order. If either string contains another other character then error should be displayed. Example:
Input contains error
Error in String #1: aacgttcOgMa
Error in String #2: ggataccaSat
This is my attempt at LCS.cpp file code:
#include "LCS.h"
#include <string>
using namespace std;
bool validate(string strX, string strY)
{
string x = strX;
string y = strY;
char searchItem = 'A';
char searchItem = 'C';
char searchItem = 'G';
char searchItem = 'T';
int numOfChar = 0;
int m = strX.length();
int n = strY.length();
for (int i = 0; i < m; i++)
{
if (x[i] == searchItem)
{
numOfChar++;
}
for (int i = 0; i < n; i++)
{
if (y[i] == searchItem)
{
numOfChar++;
}
}
}
This is my LCS.h file code:
#pragma once
#ifndef LCS_H
#define LCS_H
#include <string>
using namespace std;
bool validate(string strX, string strY);
#endif
And my driver file "Driver6.cpp" has this code:
#include "LCS.h"
#include <iostream>
#include <string>
using namespace std;
int main()
{
string strX, strY;
cout << "String #1: ";
cin >> strX;
cout << "String #2: ";
cin >> strY;
//validate the input two strings
if (validate(strX, strY) == false)
{
return 0;
}
int m = strX.length();
int n = strY.length();
}
Didn't really want to do this but it seems like the best bet rather than going round the houses in the comments:
#include <string>
#include <iostream>
bool validate( const std::string & s ) {
for ( auto c : s ) {
if ( c != 'A' && c != 'T' && c != 'C' && c != 'G' ) {
return false;
}
}
return true;
}
int main() {
std::string s1 = "ATGCCCG";
std::string s2 = "ATGfooCCCG";
if ( validate( s1 ) ) {
std::cout << "s1 is valid\n";
}
else {
std::cout << "s1 is not valid\n";
}
if ( validate( s2 ) ) {
std::cout << "s2 is valid\n";
}
else {
std::cout << "s2 is not valid\n";
}
}
Another technique:
bool validate(const std::string& s)
{
const static std::string valid_letters("ATCGatcg");
for (auto c: s)
{
std::string::size_type position = valid_letters.find_first_of(c);
if (position == std::string::npos)
{
return false;
}
}
return true;
}
The above code searches a container of valid letters.

Palindrome finder: non-alphanumeric character deletion problems

So I'm having a substantial amount of trouble with this one bit of code. I've included the whole program for context, but my issue lies in the cleanUp function, wherein I (attempt to) remove all characters that are not 'A' through 'Z'.
Any tips?
#include <iostream>
#include <cstdlib>
#include <string>
#include <stdio.h>
#include <ctype.h>
using namespace std;
bool again(string title); // Checks if you want to run again.
void makeUpper(char word[]);
void getReverse(char word[], char reversed[]);
char * find(char *str, char what);
bool equal(char word[], char reversed[]);
int size(char word[]);
char * cleanUp(char *str);
int main()
{
char word[256] = "Hello?? There!", reversedWord[256];
do
{
cout<<"Please enter the string to check: ";
makeUpper(word);
cout << word;
cleanUp(word);
getReverse(word,reversedWord);
if(equal(word, reversedWord))
cout<<"You have a palindrome!"<<endl;
else
cout<<"You do not have a palindrome!"<<endl;
} while(again("Do you want to do this again? "));
return 0;
}
bool again(string title)
{
string answer;
cout<<endl<<title;
getline(cin,answer);
return toupper(answer[0]) == 'Y';
}
void makeUpper(char word[])
{
char *ptr = word;
while (*ptr) {
*ptr = toupper(*ptr);
ptr++;
}
cout << "In uppercase:: " << word << endl;
}
char * cleanUp(char * astrid)
{
char *new_astrid;
for (*astrid; *astrid != '\0'; astrid++)
{
cout << "First loop";
if (isalpha(*astrid))
{
*new_astrid = *astrid;
new_astrid = ++new_astrid;
cout << "Here!";
}
}
cout << *new_astrid;
return *new_astrid;
}
void getReverse(char word[], char reversed[])
{
char *ptr_ind = find(word, '\0'), *ptr_ind_2 = reversed;
while(ptr_ind != word-1)
{
*ptr_ind_2 = *ptr_ind;
ptr_ind--;
ptr_ind_2++;
}
*ptr_ind_2 = '\0';
}
char * find(char *str, char what)
{
char *ptr = str;
while(*ptr != what && *ptr != '\0')
ptr++;
return *ptr == what ? ptr : NULL;
}
bool equal(char word[], char reverse[])
{
int total;
char * ptr;
ptr = word;
if((total = size(word)) != size(reverse))
return false;
for(char * ptr2 = reverse; *ptr != '\0' && *ptr == *ptr2; ptr++, ptr2++);
return *ptr == '\0';
}
int size(char word[])
{
int total = 0;
char * ptr = word;
while(*ptr != '\0') //while(!ptr)
{
ptr++;
total++;
}
return total;
}
There are several errors in your code.
new_astrid is not initialized and when you call *new_astrid = *astrid you try to copy a character to uninitialized memory, which will crash the program.
You also return the dereferenced pointer to new_astrid but the function prototype of cleanUp says that you return a pointer to char.
You should initialize new_astrid with new char[strlen(astrid)]. But then your code will result in memory leaks, since you increase the pointer (new_astid = ++new_astrid). So you should store the pointer first, so you can delete it later.
Instead of using raw pointers, i would suggest you use std::strings.
My suggestion for a palindrome tester would be:
#include <iostream>
#include <string>
#include <locale>
bool isPalindrome(std::string word)
{
std::locale loc;
for (std::string::size_type i = 0; i < word.length() / 2 + 1; ++i)
{
if (std::toupper(word[i],loc) != std::toupper(word[word.length() - i - 1],loc))
{
return false;
}
}
return true;
}
int main(int , char **)
{
std::string str = "Abba";
//Remove all non alpha values from string
str.erase(std::remove_if(str.begin(), str.end(), [](char const c){return !std::isalpha(c);}), str.end());
if (isPalindrome(str) == false)
{
std::cout << str << " is no palindrome" << std::endl;
}
else
{
std::cout << str << " is a palindrome" << std::endl;
}
return 0;
}
The erasion of non alpha values in the string is from this question.

Crashing when objects are deleted

It's crashing at the very end of the main() function where it needs to delete the starters objects. The error message that pops up when I run the program says: Debug assertion failed! Expression: _BLOCK_IS_VALID(pHead->nBlockUse). How do i fix it from crashing when deleting the starters objects?
#include <iostream>
#include <fstream>
#include "olympic.h"
using namespace std;
ofstream csis;
int main() {
const int lanes = 4;
Ranker rank(lanes);
csis.open("csis.txt");
// First make a list of names and lane assignments.
Competitor* starters[lanes];
starters[0] = new Competitor("EmmyLou Harris", 1);
starters[1] = new Competitor("Nanci Griffith", 2);
starters[2] = new Competitor("Bonnie Raitt", 3);
starters[3] = new Competitor("Joni Mitchell", 4);
// The race is run; now assign a time to each person.
starters[0]->setTime((float)12.0);
starters[1]->setTime((float)12.8);
starters[2]->setTime((float)11.0);
starters[3]->setTime((float)10.3);
// Put everyone into the ranker.
for (int i = 0; i < lanes; i++)
rank.addList(starters[i]);
// Now print out the list to make sure its right.
cout << "Competitors by lane are:" << endl;
csis << "Competitors by lane are:" << endl;
for (int i = 1; i <= lanes; i++)
rank.getLane(i)->print();
// Finally, show how they finished.
cout << "Rankings by finish are:" << endl;
csis << "Rankings by finish are:" << endl;
for (int i = 1; i <= lanes; i++)
rank.getFinish(i)->print();
for (int i = 0; i < lanes; i++)
delete starters[i];
csis.close();
}
ranker.cpp:
#include "ranker.h"
#include "competitor.h"
#include <stdlib.h>
Ranker::Ranker(int lanes) {
athlete = new Competitor*[lanes];
numAthletes = 0;
maxAthletes = lanes;
}
int Ranker::addList(Competitor* starter) {
if (numAthletes < maxAthletes && starter != NULL) {
athlete[numAthletes] = starter;
numAthletes++;
return numAthletes;
}
else
return 0;
}
Competitor* Ranker::getLane(int lane) {
for (int i = 0; i < numAthletes; i++) {
if (athlete[i]->getLane() == lane) {
return athlete[i];
}
}
return NULL;
}
Competitor* Ranker::getFinish(int position) {
switch(position) {
case 1:
return athlete[3];
break;
case 2:
return athlete[2];
break;
case 3:
return athlete[1];
break;
case 4:
return athlete[0];
break;
}
return NULL;
}
int Ranker::getFilled() {
return numAthletes;
}
Ranker::~Ranker() {
delete [] athlete;
}
competitor.h:
#ifndef _COMPETITOR_H
#define _COMPETITOR_H
class Competitor {
private:
char* name;
int lane;
double time;
public:
Competitor(char* inputName, int inputLane);
Competitor();
void setTime(double inputTime);
char* getName();
int Competitor::getLane();
double getTime();
void print();
~Competitor();
};
#endif
competitor.cpp:
#include "competitor.h"
#include <string>
#include <iostream>
#include <iomanip>
using namespace std;
Competitor::Competitor(char* inputName, int inputLane) {
name = inputName;
lane = inputLane;
}
Competitor::Competitor() {
name = 0;
lane = 0;
time = 0;
}
void Competitor::setTime(double inputTime) {
time = inputTime;
}
char* Competitor::getName() {
return name;
}
int Competitor::getLane() {
return lane;
}
double Competitor::getTime() {
return time;
}
void Competitor::print() {
cout << setw(20) << name << setw(20) << lane << setw(20) << setprecision(4) << time << endl;
}
Competitor::~Competitor() {
delete [] name;
}
Call stack:
before crash: http://i.imgur.com/d4sKbKV.png
after crash: http://i.imgur.com/C5cXth9.png
After you've added Competitor class, it seems the problem is that you delete its name in Competitor's destructor. But you assign it from string literal which can't really be deleted. I'm sure the stack trace leading to assertion will prove that.
One way of solving the problem would be using std::string to store the name.
Problem is when deleting the char* value on destructor, which is assigned with const char instead new char. So i have slightly changed the constructor to copy the const char to new char.
Competitor::Competitor(char* inputName, int charlen, int inputLane)
{
name = new char[charlen + 1];
memcpy(name , inputName, charlen );
name [charlen] = '\0';
lane = inputLane;
}

Queue Simulation problem

My program is to print the queue of information from a file but i have problem with my following code. When i run the program it keep loop. I cant figure out the problem. Any help?
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
#include <queue>
#include <list>
using namespace std;
void simulation(ifstream &infile);
void processArrival(int *newEvent, ifstream &inFile, list<int> eventList,queue<int> printQueue);
void processDeparture(int *newEvent, list<int> eventList,queue<int> printQueue);
string name[100];
int timeAccepted[100];
int fileSize[100];
int i = 1;
int j = 1;
int currentTime;
bool checker = true;
int main(void)
{
ifstream inFile;
string fileName;
int i = 0;
inFile.open("123.txt", ios::in);
simulation(inFile);
/*while(inFile.peek() != EOF )
{
inFile>>name[i]>>timeAccepted[i]>>fileSize[i];
i++;
}
for(int s = 0; s < i; s++)
{
cout << name[s] << timeAccepted[s] << fileSize[s] <<endl;
}*/
return 0;
}
void simulation(ifstream &inFile)
{
queue<int> printQueue;
list<int> eventList;
int *newEvent;
while(inFile.peek() != '\n')
{
inFile>>name[0]>>timeAccepted[0]>>fileSize[0];
}
eventList.push_front(timeAccepted[0]);
int checkEmpty = eventList.empty();
newEvent = &eventList.front();
while(checkEmpty ==0)
{
newEvent = &eventList.front();
if(checker)
{
processArrival(newEvent, inFile, eventList, printQueue);
}
else
{
processDeparture(newEvent, eventList, printQueue);
}
checkEmpty = eventList.empty();
}
}
void processArrival(int *newEvent, ifstream &inFile, list<int> eventList,queue<int> printQueue)
{
int atFront=0;
atFront = printQueue.empty();
cout << atFront <<endl;
printQueue.push(*newEvent);
cout << printQueue.front() <<endl;
eventList.remove(*newEvent);
int temp;
if(atFront==1)
{
currentTime = *newEvent + fileSize[0];
cout << name[0] << " ## " << *newEvent << " ## " << currentTime << endl;
eventList.push_back(currentTime);
}
checker = false;
if(inFile.peek() != EOF )
{
inFile>>name[i]>>timeAccepted[i]>>fileSize[i];
eventList.push_back( timeAccepted[i] );
i++;
checker = false;
if(eventList.back() <= eventList.front())
{
temp = eventList.back();
eventList.back() = eventList.front();
eventList.front() = temp;
checker = true;
}
}
}
void processDeparture(int *newEvent, list<int> eventList,queue<int> printQueue)
{
printQueue.pop();
eventList.pop_front();
int checkEmpty = 1;
checkEmpty = printQueue.empty();
int temp;
if(checkEmpty ==0)
{
currentTime = *newEvent + fileSize[j];
cout << name[j] << " " << *newEvent << " " << currentTime << endl;
eventList.push_back(currentTime);
checker = true;
if(eventList.back() < eventList.front())
{
temp = eventList.back();
eventList.back() = eventList.front();
eventList.front() = temp;
checker = false;
}
j++;
}
}
Your processArrival and processDeparture functions are taking their eventList and printQueue arguments by value. This means that when you call them, for example in this line:
processArrival(newEvent, inFile, eventList, printQueue);
Copies of eventList and printQueue are made and passed into the processArrival function. The processArrival function then operates on those copies, and the original data is never modified. In particular, this means that the original eventList will never have any items removed from it, so it will never be empty -- it will just keep trying to process the first event over and over again.
The solution is to pass these parameters by reference. i.e. change the definition of processArrival to
void processArrival(int *newEvent, ifstream &inFile, list<int>& eventList, queue<int>& printQueue)
Note the & characters that I have inserted before eventList and printQueue. These cause references to the original data, rather than copies of the original data, to be passed into the processArival function. This means that processArrival will operate directly on the original data as you intend it to. Don't forget to make the corresponding change to processDeparture as well.