Input from text file into char *array[9] - c++

I have a file with 9 words and i have to store each word into the char array of 9 pointers but i keep getting an error message. I cannot use vectors!
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
char *words[9];
ifstream inStream;
inStream.open("sentence.txt");
if (inStream.fail())
{
cout << "Input file opening failed.\n";
exit(1);
}
for ( int i = 0; i < 10; i++)
{
inStream >> words[i];
}
inStream.close();
return 0;
}

The declaration
char *words[9];
declares a raw array of pointers. This array is not initialized so the pointers have indeterminate values. Using any of them would be Undefined Behavior.
Instead you want
vector<string> words;
where vector is std::vector from the <vector> header, and string is std::string from the <string> header.
Use the push_back member function to add strings to the end of the vector.
Also you need to move the close call out of the loop. Otherwise it will close the file in the first iteration.
This approach gives the code (off the cuff, disclaimer...)
#include <fstream>
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector<string> words;
ifstream inStream;
inStream.open("sentence.txt");
for ( int i = 0; i < 10; i++)
{
string word;
if( inStream >> word )
words.push_back( word );
}
inStream.close();
}
If you can't use std::string and std::vector then you need to initialize the array of pointers, and make sure that you don't read more into the buffers than there's room for.
The main problem here is that >> is unsafe for reading into a raw array given by a pointer. It doesn't know how large that array is. It can easily lead to a buffer overrun, with dire consequences.
And so this gets a bit complicated, but it can look like this:
#include <ctype.h> // isspace
#include <fstream>
#include <iostream>
#include <locale.h> // setlocale, LC_ALL
#include <stdlib.h> // EXIT_FAILURE
using namespace std;
void fail( char const* const message )
{
cerr << "! " << message << "\n";
exit( EXIT_FAILURE );
}
void readWordFrom( istream& stream, char* const p_buffer, int const buffer_size )
{
int charCode;
// Skip whitespace:
while( (charCode = stream.get()) != EOF and isspace( charCode ) ) {}
int n_read = 0;
char* p = p_buffer;
while( n_read < buffer_size - 1 and charCode != EOF and not isspace( charCode ) )
{
*p = charCode; ++p;
++n_read;
charCode = stream.get();
}
*p = '\0'; // Terminating null-byte.
if( charCode != EOF )
{
stream.putback( charCode );
if( not isspace( charCode ) )
{
assert( n_read == buffer_size - 1 ); // We exceeded buffer size.
stream.setstate( ios::failbit );
}
}
}
int main()
{
static int const n_words = 9;
static int const max_word_length = 80;
static int const buffer_size = max_word_length + 1; // For end byte.
char *words[n_words];
for( auto& p_word : words ) { p_word = new char[buffer_size]; }
ifstream inStream{ "sentence.txt" };
if( inStream.fail() ) { fail( "Input file opening failed." ); }
setlocale( LC_ALL, "" ); // Pedantically necessary for `isspace`.
for( auto const p_word : words )
{
readWordFrom( inStream, p_word, buffer_size );
if( inStream.fail() ) { fail( "Reading a word failed." ); }
}
for( auto const p_word : words ) { cout << p_word << "\n"; }
for( auto const p_word : words ) { delete[] p_word; }
}

You never allocate any memory for your char* pointers kept in the array.
The idiomatic way to write a c++ code would be:
#include <iostream>
#include <fstream>
#include <vector>
int main() {
std::vector<std::string> words(9);
std::ifstream inStream;
inStream.open("sentence.txt");
for ( int i = 0; inStream && i < 9; i++) {
inStream >> words[i];
}
}
The inStream.close() isn't necessary, and even wrong inside the loop. The std::istream will be closed automatically as soon the variable goes out of scope.

There are a few problems with your code.
char *words[9];
This allocates space for 9 pointers, not nine strings. Since you don't know how big the strings are you have two choices. You can either "guess" how much you'll need and limit the inputs accordingly, or you can use dynamic memory allocation (malloc or new) to create the space you need to store the strings. Dynamic memory would be my choice.
for ( int i = 0; i < 10; i++)
This loop will execute on words[0] through words[9]. However, there is no words[9] (that would be the tenth word) so you'll overwrite memory that you have not allocated
inStream >> words[i];
This will send your input stream to memory that you don't "own". You need to allocate space for the words to live before capturing them from the input stream. To do this correctly, you'll need to know how much space each word will need so you can allocate it.
you could try something like this:
int main()
{
char *words[9];
char tempInput[256]; // space to capture the input, up to a maximum size of 256 chars
ifstream inStream;
inStream.open("sentence.txt");
if (inStream.fail())
{
cout << "Input file opening failed.\n";
exit(1);
}
for ( int i = 0; i < 9; i++)
{
//Clear the input buffer
memset(tempInput, 0, 256);
//Capture the next word
inStream >> tempInput;
//allocate space to save the word
words[i] = new char(strlen(tempInput));
//Copy the word to its final location
strcpy(words[i], tempInput)
}
inStream.close();
return 0;
}

Related

i am trying to use getline to read a csv file line by line and separate the data in the file and turn a string into int

I am a beginner and I just need a bit of help on why I getline is showing an error:
this is what I have so far
#include <iostream>
#include <iomanip>
#include <cmath>
#include <fstream>
using namespace std;
const double TAX_RATE = 0.0825;
const int MAX_ITEMS = 1000;
const int MAX_TRANSACTIONS = 100;
int main(int argc, char const *argv[]){
string fname = "";
int itemCnt = 0, start = 0, end = 0;
int ids[MAX_ITEMS], qtys[MAX_ITEMS];
double costs[MAX_ITEMS], subtotals[MAX_TRANSACTIONS],
taxes[MAX_TRANSACTIONS], totals[MAX_TRANSACTIONS];
string names[MAX_ITEMS], paymentTypes[MAX_ITEMS], payments[MAX_ITEMS];
ifstream iFile;
if ( argc != 2 ) {
cout<<"usage: "<< argv[0]<< " <file name>" <<endl;
return 0;
} else {
iFile.open(argv[1]);
}
if (!iFile) {
cout<<"Error: Invalid file name"<<endl;
cin.clear();
}
while (!iFile.eof())
{
getline(iFile,str); //this isn't working
int commaLoc = str.find(',');
ids[itemCnt]= str.substr(0,commaLoc);
str = str.substr(commaLoc +1, str.length());
//string to int I'm not sure how to do I know its something with stoi() but not sure how to format it
}
return 0;
}
I am able to get the file to open but I'm not sure why getline isn't working it keeps saying something like
no instance of overload function
My csv file looks like:
1,Laptop,799.99,1,cash,1100
I need it to read the first number and because Its a string i don't know how to save it as an int
Multiple errors. First there is nothing called 'str' in your program. I will guess its just a string used as a temp buffer
do not do this (!File.eof) it doesnt do what you think.
while (iFile)
{
string str; <<<<<==== added
getline(iFile,str); //this isn't working <<<===is now
int commaLoc = str.find(',');
Next this line doesnt work because ids are ints and substring returns a string.
// ids[itemCnt]= str.substr(0,commaLoc);
ids[itemCnt]= stoi(str.substr(0,commaLoc)); <<<<==== fixed
str = str.substr(commaLoc +1, str.length());
}
I strongly recommend you use std::vector instead of c-style fixed size arrays. Takes 5 minutes to learn how to use them and they have huge benefits. If you must use fixed size arrays use std::array instead of c-style
You can read a string and try to convert it to a number in different ways. For example, since C++17, you can use from_chars. One of its overloads:
Receives a pair of begin and end char pointers, and an int variable,
tries to parse an int number, and
and returns the parsed number, together with a pointer to the first character that wasn't part of the match.
int i{};
auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), i);
if (ec == std::errc{}) { /* do something with i */} else { /* error */ }
[Demo]
Full code (using a istrinstream instead of a ifstream):
#include <charconv> // from_chars
#include <iomanip>
#include <iostream>
#include <sstream> // istringstream
#include <system_error> // errc
constinit const int MAX_ITEMS = 10;
int main() {
std::istringstream iss{
"1,Laptop,799.99,1,cash,1100\n"
"2,PC,688.88,2,card,1101\n"
"blah,Keyboard,39.00,3,cash,1102"
};
size_t itemCnt{};
int ids[MAX_ITEMS]{};
std::string str{};
while (std::getline(iss, str)) {
// Parse counter
int i{};
auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), i);
if (ec == std::errc{}) {
ids[itemCnt] = i;
// Remaining string
std::string remaining_string{ str.substr(ptr - str.data() + 1) };
std::cout << ids[itemCnt] << ", " << remaining_string << "\n";
}
else {
std::cout << "Error: invalid counter.\n";
}
++itemCnt;
}
}
// Outputs:
//
// 1, Laptop,799.99,1,cash,1100
// 2, PC,688.88,2,card,1101
// Error: invalid counter.

Cant understand how to use seekg tellg to achieve the following result

As part of a larger program, my task is to read each line of an input file and store the index offset to each line. Later given all the index offsets, I want to be able to go to that position in the file directly and print the line corresponding to that offset. Can someone help me in figuring out what I am doing incorrectly in the following code.
#include <iostream>
#include <sstream>
#include <vector>
#include <iterator>
#include <stdio.h>
#include <string.h>
void create_index_info(ifstream& myFile, std::unordered_map<size_t, std::pair<std::streampos, size_t>>& map_index_start_end_pos)
{
size_t uiLength;
size_t uiCount = 0;
std::string line;
while (getline(myFile, line))
{
start_pos = myFile.tellg();
uiLength = strlen(line.c_str());
map_index_start_end_pos.emplace( uiCount, std::make_pair(start_pos, uiLength) );
uiCount++;
}
}
void print_index_info(ifstream& myFile, const std::unordered_map<size_t, std::pair<std::streampos, size_t>>& map_index_start_end_pos)
{
size_t uiLength;
for(auto it = map_index_start_end_pos.begin(); it != map_index_start_end_pos.end(); it++)
{
auto res = it->second;
myFile.clear();
myFile.seekg(res.first, ios::beg);
uiLength = res.second;
char* buffer = (char*)malloc(uiLength * sizeof(char));
myFile.read(buffer, uiLength);
for(size_t uiCount = 0; uiCount < uiLength; uiCount++)
{
std::cout<< buffer[uiCount];
}
std::cout<<"\n";
free(buffer);
buffer = NULL;
}
}
int main()
{
std::unordered_map<size_t, std::pair<std::streampos, size_t>> map_index_start_end_pos;
ifstream myFile("Filename.txt");
create_index_info(myFile, map_index_start_end_pos);
myFile.close();
ifstream myFile1("Filename.txt");
print_index_info(myFile1, map_index_start_end_pos);
myFile1.close();
return 0;
}
The data in the input text file contains the following entries:
9 10 11
8 7 5
67 34 12 45 9
20
The ideal output for the code should be same as the input modulo upto ordering of the lines being printed (I am using an unordered map, so the output need not be printed in the same order as it was fed). However my code is outputting some garbage, mostly characters of the form \x00. Can someone please help me figure out what is wrong in my code.
Your program does not compile.
Why are you using a map? Why a vector is not enough?
Why getline and not ignore? ignore is faster and does not allocate memory.
You are probably looking for this:
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
std::istream& get_line_idx( std::istream& is, std::vector<std::streampos>& result )
{
for( std::streampos pos = is.tellg(); is.ignore( std::numeric_limits<std::streamsize>::max(), '\n' ); pos = is.tellg() )
result.push_back( pos );
return is;
}
void print( std::istream& is, std::vector<std::streampos>& pos )
{
for( auto curpos : pos )
{
std::string line;
is.seekg( curpos );
std::getline( is, line );
std::cout << line << std::endl;
}
}
int main()
{
std::vector<std::streampos> result;
{
std::ifstream is { "c:\\temp\\test.txt" };
if( !is )
return -1;
get_line_idx( is, result );
}
{
std::ifstream is { "c:\\temp\\test.txt" };
if( !is )
return -2;
print( is, result );
}
return 0;
}

How to start reading file from a particular position c++

I am reading a file using fstream and getline functions. I want to give a starting position e.g. my file has 13 lines I want to start reading it from 7th line for example. Here is my code:
#include<iostream>
#include <stdlib.h>
#include <string>
#include <vector>
#include<iterator> // for iterators
#include<map>
using namespace std;
int main()
{
string line;
int start= 7;
unsigned long int index;
For( int z=1; z<=13; z++){
if (f_node.is_open())
{
getline(f_node, line);
if ((line.find("$EndNodes") != string::npos))
{
cout << "$EndNodes found file closed .... " << endl;
f_node.close();
return false;
}
// Point index.
int i = 0;
int j = line.find_first_of(" ", i);
index = strtoul((line.substr(i, j)).c_str(), NULL, 0);//
}
}
I am reading only indexes and I want to start it from 7th index How to do it?
To discard some number of lines, something like:
#include <fstream>
#include <string>
int main() {
std::ifstream infile{"myfile.txt"};
std::string line;
int starting_line = 7;
// Read and discard beginning lines
for (int n = 1; n < starting_line; n += 1) {
if (!std::getline(infile, line)) {
// Error or premature end of file! Handle appropriately.
}
}
while (std::getline(infile, line)) {
// Do something with the lines you care about.
}
return 0;
}
Except with actual error checking and handling and such.
"there is no way to tell code the starting position like seekg and tellg?" No. NL is just like any other character, it does not receive any special treatment.
You simply must scan the stream, counting the new-line character:
std::istream& seek_line(std::istream& is, const int n, std::ios_base::seekdir way = std::ios_base::beg)
{
is.seekg(0, way);
int i = 0;
char c;
while (is.get(c) && i < n)
if (c == '\n')
++i;
is.putback(c);
return is;
}
And this is how you use the above function:
int main()
{
using namespace std;
ifstream is{ "c:\\temp\\test.txt" };
if (!is)
return -1;
if (!seek_line(is, 3))
return -2;
string s;
getline(is, s);
cout << s << endl;
return 0;
}

I need help explaining the behaviour of this code

I've been having a lot of trouble with the concept of an array of struct. I put together some basic code. The output from this code was not what I expected at all. I was wondering if someone could explain why this code behaves the way it does.
#include <iostream>
#include <cstdlib>
#include <string>
#include <sstream>
struct DataRow
{
std::string word1;
std::string word2;
std::string word3;
};
void getRow( std::string line, DataRow** dataRowPointer, int index )
{
std::stringstream sline(line);
std::string word;
int wordIndex = 0;
*dataRowPointer = new DataRow[3];
while( sline >> word )
{
if ( wordIndex == 0 )
{ (*dataRowPointer)[index].word1 = word; }
else if ( wordIndex == 1 )
{ (*dataRowPointer)[index].word2 = word; }
else
{ (*dataRowPointer)[index].word3 = word; }
wordIndex++;
}
}
int main( )
{
int index = 0;
std::string line1 = "This is line";
std::string line2 = "Two is magic";
std::string line3 = "Oh Boy, Hello";
DataRow* dataRowPointer;
getRow( line1, &dataRowPointer, index );
index++;
getRow( line2, &dataRowPointer, index );
index++;
getRow( line3, &dataRowPointer, index );
for( int i = 0; i < 3; i++ )
{
std::cout << dataRowPointer[i].word1 << dataRowPointer[i].word2 << dataRowPointer[i].word3 << "\n";
}
return 0;
}
There are 3 strings. I want to separate each individual word within each string and store them into a structure. I have an array of structures to store them. The size of the array is 3 (since there are 3 lines). I do not set the pointer in main, I set the pointer in my function. From there I start collecting my words to store.
I obtain this output:
(blank line)
(blank line)
OhBoy,Hello
My question is, where did my first two structures go?
Your getRow is re-allocating the DataRow array on each invocation, and thus you're losing the results of the first two invocations. Move the allocation into your main().

Printing input string words in reverse order

Using if and while/do-while, my job is to print following user's inputs (string value) in reverse order.
For example:
input string value : "You are American"
output in reverse order : "American are You"
Is there any way to do this?
I have tried
string a;
cout << "enter a string: ";
getline(cin, a);
a = string ( a.rbegin(), a.rend() );
cout << a << endl;
return 0;
...but this would reverse the order of the words and spelling while spelling is not what I'm going for.
I also should be adding in if and while statements but do not have a clue how.
The algorithm is:
Reverse the whole string
Reverse the individual words
#include<iostream>
#include<algorithm>
using namespace std;
string reverseWords(string a)
{
reverse(a.begin(), a.end());
int s = 0;
int i = 0;
while(i < a.length())
{
if(a[i] == ' ')
{
reverse(a.begin() + s, a.begin() + i);
s = i + 1;
}
i++;
}
if(a[a.length() - 1] != ' ')
{
reverse(a.begin() + s, a.end());
}
return a;
}
Here is a C-based approach that will compile with a C++ compiler, which uses the stack to minimize creation of char * strings. With minimal work, this can be adapted to use C++ classes, as well as trivially replacing the various for loops with a do-while or while block.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LINE_LENGTH 1000
#define MAX_WORD_LENGTH 80
void rev(char *str)
{
size_t str_length = strlen(str);
int str_idx;
char word_buffer[MAX_WORD_LENGTH] = {0};
int word_buffer_idx = 0;
for (str_idx = str_length - 1; str_idx >= 0; str_idx--)
word_buffer[word_buffer_idx++] = str[str_idx];
memcpy(str, word_buffer, word_buffer_idx);
str[word_buffer_idx] = '\0';
}
int main(int argc, char **argv)
{
char *line = NULL;
size_t line_length;
int line_idx;
char word_buffer[MAX_WORD_LENGTH] = {0};
int word_buffer_idx;
/* set up line buffer - we cast the result of malloc() because we're using C++ */
line = (char *) malloc (MAX_LINE_LENGTH + 1);
if (!line) {
fprintf(stderr, "ERROR: Could not allocate space for line buffer!\n");
return EXIT_FAILURE;
}
/* read in a line of characters from standard input */
getline(&line, &line_length, stdin);
/* replace newline with NUL character to correctly terminate 'line' */
for (line_idx = 0; line_idx < (int) line_length; line_idx++) {
if (line[line_idx] == '\n') {
line[line_idx] = '\0';
line_length = line_idx;
break;
}
}
/* put the reverse of a word into a buffer, else print the reverse of the word buffer if we encounter a space */
for (line_idx = line_length - 1, word_buffer_idx = 0; line_idx >= -1; line_idx--) {
if (line_idx == -1)
word_buffer[word_buffer_idx] = '\0', rev(word_buffer), fprintf(stdout, "%s\n", word_buffer);
else if (line[line_idx] == ' ')
word_buffer[word_buffer_idx] = '\0', rev(word_buffer), fprintf(stdout, "%s ", word_buffer), word_buffer_idx = 0;
else
word_buffer[word_buffer_idx++] = line[line_idx];
}
/* cleanup memory, to avoid leaks */
free(line);
return EXIT_SUCCESS;
}
To compile with a C++ compiler, and then use:
$ g++ -Wall test.c -o test
$ ./test
foo bar baz
baz bar foo
This example unpacks the input string one word at a time,
and builds an output string by concatenating in reverse order.
`
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
string inp_str("I am British");
string out_str("");
string word_str;
istringstream iss( inp_str );
while (iss >> word_str) {
out_str = word_str + " " + out_str;
} // while (my_iss >> my_word)
cout << out_str << endl;
return 0;
} // main
`
This uses exactly one each of if and while.
#include <string>
#include <iostream>
#include <sstream>
void backwards(std::istream& in, std::ostream& out)
{
std::string word;
if (in >> word) // Read the frontmost word
{
backwards(in, out); // Output the rest of the input backwards...
out << word << " "; // ... and output the frontmost word at the back
}
}
int main()
{
std::string line;
while (getline(std::cin, line))
{
std::istringstream input(line);
backwards(input, std::cout);
std::cout << std::endl;
}
}
You might try this solution in getting a vector of string's using the ' ' (single space) character as a delimiter.
The next step would be to iterate over this vector backwards to generate the reverse string.
Here's what it might look like (split is the string splitting function from that post):
Edit 2: If you don't like vectors for whatever reason, you can use arrays (note that pointers can act as arrays). This example allocates a fixed size array on the heap, you may want to change this to say, double the size when the current word amount has reached a certain value.
Solution using an array instead of a vector:
#include <iostream>
#include <string>
using namespace std;
int getWords(string input, string ** output)
{
*output = new string[256]; // Assumes there will be a max of 256 words (can make this more dynamic if you want)
string currentWord;
int currentWordIndex = 0;
for(int i = 0; i <= input.length(); i++)
{
if(i == input.length() || input[i] == ' ') // We've found a space, so we've reached a new word
{
if(currentWord.length() > 0)
{
(*output)[currentWordIndex] = currentWord;
currentWordIndex++;
}
currentWord.clear();
}
else
{
currentWord.push_back(input[i]); // Add this character to the current word
}
}
return currentWordIndex; // returns the number of words
}
int main ()
{
std::string original, reverse;
std::getline(std::cin, original); // Get the input string
string * arrWords;
int size = getWords(original, &arrWords); // pass in the address of the arrWords array
int index = size - 1;
while(index >= 0)
{
reverse.append(arrWords[index]);
reverse.append(" ");
index--;
}
std::cout << reverse << std::endl;
return 0;
}
Edit: Added includes, main function, while loop format
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
// From the post
std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems)
{
std::stringstream ss(s);
std::string item;
while(std::getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}
std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
return split(s, delim, elems);
}
int main ()
{
std::string original, reverse;
std::cout << "Input a string: " << std::endl;
std::getline(std::cin, original); // Get the input string
std::vector<std::string> words = split(original, ' ');
std::vector<std::string>::reverse_iterator rit = words.rbegin();
while(rit != words.rend())
{
reverse.append(*rit);
reverse.append(" "); // add a space
rit++;
}
std::cout << reverse << std::endl;
return 0;
}
This code here uses string libraries to detect the blanks in the input stream and rewrite the output sentence accordingly
The algorithm is
1. Get the input stream using getline function to capture the spacecs. Initialize pos1 to zero.
2. Look for the first space in the input stream
3. If no space is found, the input stream is the output
4. Else, get the position of the first blank after pos1, i.e. pos2.
5. Save the sub-string bewteen pos1 and pos2 at the beginning of the output sentence; newSentence.
6. Pos1 is now at the first char after the blank.
7. Repeat 4, 5 and 6 untill no spaces left.
8. Add the last sub-string to at the beginning of the newSentence. –
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string sentence;
string newSentence;
string::size_type pos1;
string::size_type pos2;
string::size_type len;
cout << "This sentence rewrites a sentence backward word by word\n"
"Hello world => world Hello"<<endl;
getline(cin, sentence);
pos1 = 0;
len = sentence.length();
pos2 = sentence.find(' ',pos1);
while (pos2 != string::npos)
{
newSentence = sentence.substr(pos1, pos2-pos1+1) + newSentence;
pos1 = pos2 + 1;
pos2 = sentence.find(' ',pos1);
}
newSentence = sentence.substr(pos1, len-pos1+1) + " " + newSentence;
cout << endl << newSentence <<endl;
return 0;
}