What The Code Does:
The code below is supposed to format .cpp files for a printer, by limiting the number of chars in each line of code without losing any code. The code is supposed to go through the file char by char. It does so by opening an ifstream then using the get method to get each char till the EOF is found. As each char is grabbed it is pushed to a std::string (vector) as each char is pushed, a line char count is incremented. If a new line char is found before the line char count is larger than it's max size, it is reset and continues on the next line. If the line char count is 2 less than the max char count before a '\n' char is found a \, \n, ' ''s(x tab count) are appended to the end of the string being written to. Then the rest of the line is appended to the string. Though, if the last char added to the string is not a ' ' char then the string has all the chars between it's end and the closest to the left ' ' char removed and placed at the start of the new line, after the 5 ' ' chars on the new line. The last bit is where I think the error is.
Sample Line: (pretend is bigger than max char count)
LongLong123 longLineOfCode;\n;
Sample Output:
Bad:
LongLong123 longLineOfCo\\n
de;\n
Good:
LongLong123\\n
longLineOfCode;\n
The Error:
Unhandled exception at 0x55CC1949 (ucrtbased.dll) in SubmitFormater.exe: 0xC00000FD: Stack overflow (parameters:
0x00000000, 0x00F02000). occurred
Sample Command line:
110 5 "C:/Users/holycatcrusher/Documents/OC 2018 Fall/222/Labs/COSC_222_Data_Structures_Cpp/SubmitFormater.cpp"
The 110 is the number of chars that can be in a line, the 5 is the number of chars the tab is (5 blank spaces), and the long string is the address of the file to be formatted (make a copy of file as upon error the file entered will be destroyed). I am using the code that makes up this project as sample input (a copy of it).
The code:
/* SubmitFormater.cpp
This file was created by Bradley Honeyman
Oct 25, 2018
This file is used to format submit files for COSC 222 asignments
*/
#include "pch.h"
#include <iostream>
#include <fstream>
#include <string>
#include <stdbool.h>
#include <stddef.h>
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
std::string *processFile(std::ifstream *input, int maxCharCount, int tabSpaceSize, char delimiter, std::string *code, int lineCharCount);
int toInt(char *input);
int main(int argc, char **argv) {
std::cout << "Running 222 formatter" << std::endl << "by: Bradley Honeyman" << std::endl;
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
//make sure there is the min number of commands
if (argc < 3) {
std::cout << "Ensure there is a specified max line length and at least one file to modify!" << std::endl;
return 0;
}
for (int i = 3; i < argc; i++) {
//open file and make sure it opens
std::ifstream input;
input.open(argv[i]);
if (!input.is_open()) {
std::cout << "Could not open the input file: " << argv[i] << std::endl;
return EXIT_FAILURE;
}
//process code
std::string *code = new std::string();
processFile(&input, toInt(argv[1]), toInt(argv[2]), ' ', code, 0);
//check for error
if (code == NULL) {
return EXIT_FAILURE;
}
//output
input.close();
std::ofstream out(argv[i]);
if (!out.is_open()) {
std::cout << "could not write to output file" << std::endl;
return EXIT_FAILURE;
}
//print to file
std::cout << code->c_str() << std::endl;
out << code->c_str();
//close out delete code pointer
out.close();
delete code;
}
return 0;
}
/*
formats a file by placing \\\n the custom tab whereever the length of a line is bigger than the max
also a delimiter is used, so words aren't cut in half
*/
#define FORMAT_CHAR_COUNT 2
std::string *processFile(std::ifstream *input, int maxCharCount, int tabSpaceSize, char delimiter, std::string *code, int lineCharCount) {
//std::cout << "Processing" << std::endl;
//get char and check if is end of file
char current = input->get();
//std::cout << "\'" << current << "\'" << std::endl;
if (input->eof()) {
return code;
}
//incerment char count then check if are at furthest possible position
lineCharCount++;
//std::cout << lineCharCount << std::endl;
if (current == '\n') {
lineCharCount = 0;
//check if are at max char count, split code and put to new line
} else if (lineCharCount >= maxCharCount && current != '\\') {
//if not at the end of the line go back to the closest delimiter to break
std::string *pre = new std::string("");
bool fail = false;
if (current != '\n' && code->at(code->size() - 1)) {
code->push_back(current);
int i = code->size() - 1;
int back = 0;
for (i; i >= 0; i--) {
pre->push_back(code->at(i));
back++;
if (back > maxCharCount - tabSpaceSize - FORMAT_CHAR_COUNT) {
std::cout << "Can not format file because there isn't a place close enough to break at a delimiter!" << std::endl;
fail = true;
break;
}
}
//check for fail
if (!fail) {
//add delimiter if it needs to be
if (pre->size() > 0 && pre->at(pre->size() - 1) != delimiter) {
pre->push_back(delimiter);
}
//reverse prepending string, and remove code that is to be prepended
std::reverse(pre->begin(), pre->end());
code->assign(code->substr(0, code->size() - back - 1));
}
}
//insert \ then new line then tab then set char count to tab size + pre size + 1 for char to be added
code->push_back('\\');
code->push_back('\n');
for (int i = 0; i < tabSpaceSize; i++) { code->push_back(' '); } //create tab
code->append(pre->c_str());
lineCharCount = tabSpaceSize + 1 + pre->size();
pre->clear();
delete pre;
}
//add char to code and run again
code->push_back(current);
return processFile(input, maxCharCount, tabSpaceSize, delimiter, code, lineCharCount);
}
/*
converts string to an int
*/
int toInt(char *input) {
int i = 0;
int out = 0;
while (input[i] != '\0') {
out = (out * 10) + (input[i] - '0');
i++;
}
return out;
}
Also, you can use the code above code as the sample .cpp file. Do remember to use a copy though, because the program will modify the file!
Call Stack Part 1 and 2(click images to be able to read them)
Note:
The cause of the problem is covered in the conversation.
After the amount of talking that went on this post I think It is worth posting what ended up being the solution for me. Also a few notes on what I concluded.
Code:
/* SubmitFormater.cpp
This file was created by Bradley Honeyman
Oct 25, 2018
This file is used to format submit files for COSC 222 asignments
*/
#include "pch.h"
#include <iostream>
#include <fstream>
#include <string>
#include <stdbool.h>
#include <stddef.h>
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
std::string *processFile(std::ifstream *input, int maxCharCount, int tabSpaceSize, char delimiter, std::string *code, int lineCharCount);
int toInt(char *input);
int main(int argc, char **argv) {
std::cout << "Running 222 formatter" << std::endl << "by: Bradley Honeyman" << std::endl;
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
//make sure there is the min number of commands
if (argc < 3) {
std::cout << "Ensure there is a specified max line length and at least one file to modify!" << std::endl;
return 0;
}
for (int i = 3; i < argc; i++) {
//open file and make sure it opens
std::ifstream input;
input.open(argv[i]);
if (!input.is_open()) {
std::cout << "Could not open the input file: " << argv[i] << std::endl;
return EXIT_FAILURE;
}
//process code
std::string *code = new std::string();
processFile(&input, toInt(argv[1]), toInt(argv[2]), ' ', code, 0);
//check for error
if (code == NULL) {
return EXIT_FAILURE;
}
//output
input.close();
std::ofstream out(argv[i]);
if (!out.is_open()) {
std::cout << "could not write to output file" << std::endl;
return EXIT_FAILURE;
}
//print to file
out << code->c_str();
//close out delete code pointer
out.close();
delete code;
}
return 0;
}
/*
formats a file by placing \\\n the custom tab whereever the length of a line is bigger than the max
also a delimiter is used, so words aren't cut in half
*/
#define FORMAT_CHAR_COUNT 2
std::string *processFile(std::ifstream *input, int maxCharCount, int tabSpaceSize, char delimiter, std::string *code, int lineCharCount) {
//get char and check if is end of file
char current = input->get();
if (input->eof()) {
return code;
}
//incerment char count then check if are at furthest possible position
lineCharCount++;
//std::cout << lineCharCount << std::endl;
if (current == '\n') {
lineCharCount = 0;
//check if are at max char count, split code and put to new line
} else if (lineCharCount >= maxCharCount && current != '\\') {
//if not at delimiter push everything to the right of the nearest delimiter to the left to pre
int back = 0;
std::string pre("");
if (current != delimiter) {
for (int i = code->size() - 1; i >= 0; i--) {
back++;
if (code->at(i) == delimiter) {
pre.push_back(code->at(i));
break;
} else {
pre.push_back(code->at(i));
}
}
//remove what was added to pre from code
std::reverse(pre.begin(), pre.end());
code->assign(code->substr(0, code->size() - back));
}
//insert \ then new line then tab then set char count to tab size + pre size + 1 for char to be added
code->push_back('\\');
code->push_back('\n');
for (int i = 0; i < tabSpaceSize; i++) { code->push_back(' '); } //create tab
code->append(pre);
lineCharCount = tabSpaceSize + 1 + pre.size();
}
//add char to code and run again
code->push_back(current);
return processFile(input, maxCharCount, tabSpaceSize, delimiter, code, lineCharCount);
}
/*
converts string to an int
*/
int toInt(char *input) {
int i = 0;
int out = 0;
while (input[i] != '\0') {
out = (out * 10) + (input[i] - '0');
i++;
}
return out;
}
Notes:
The code only runs when compiled as a release version in VS
There was a logical error in the while loop
Compiler Optimization seems to solve the problem
Related
I've got a large text document that including adjacent numbers and letters.
Just like that,
JACK1940383DAVID30284HAROLD68372TROY4392 etc.
How can i split this like below in C++
List: Jack / 1940383 , David/30284, ...
You can use std::string::find_first_of() and std::string::find_first_not_of() in a loop, using std::string::substr() to extract each piece, eg:
std::string s = "JACK1940383DAVID30284HAROLD68372TROY4392";
std::string::size_type start = 0, end;
while ((end = s.find_first_of("0123456789", start)) != std::string::npos) {
std::string name = s.substr(start, end-start);
start = end;
int number;
if ((end = s.find_first_not_of("0123456789", start)) != std::string::npos) {
number = std::stoi(s.substr(start, end-start));
}
else {
number = std::stoi(s.substr(start));
}
start = end;
// use name and number as needed...
}
Online Demo
You can use regex like this:
#include <iostream>
#include <string>
#include <regex>
#include <vector>
// create a struct to group your data
// this makes it easy to store it in a vector.
struct person_t
{
std::string name;
std::string number;
};
// overloaded output operator for printing one person's details
std::ostream& operator<<(std::ostream& os, const person_t& person)
{
std::cout << person.name << ": " << person.number << std::endl;
return os;
}
// get a vector of person_t based on the input
auto get_persons(const std::string& input)
{
// make a regex in this case a regex that will match one or more capital letters
// and groups them using the ()
// then match one or more digits and group them too.
static const std::regex rx{ "([A-Z]+)([0-9]+)" };
std::smatch match;
// a vector to hold all the persons
std::vector<person_t> persons;
// start at begin of string and look for first part of the string
// that matches the regex.
auto cbegin = input.cbegin();
while (std::regex_search(cbegin, input.cend(), match, rx))
{
// match[0] will contain the whole match,
// match[1]-match[n] will contain the groups from the regular expressions
// match[1] will contain the match with characters and thus the name
// match[2] will contain the match with the numbers and thus the number.
// create a person_t struct with this info
person_t person{ match[1], match[2] };
// and add it to the vector
persons.push_back(person);
cbegin = match.suffix().first;
}
return persons;
}
int main()
{
// parse and split the string
auto persons = get_persons("JACK1940383DAVID30284HAROLD68372TROY4392");
// show the output
for (const auto& person : persons)
{
std::cout << person;
}
}
As pointed in other good answers you can use
find_first_of(), find_first_not_of() and substr() from std::string in a loop
regex
But it may be too much. I will add 3 more examples that you may find
simpler.
The first 2 programs expects the file name on the command line for (my) convenience here, and the test file is in.txt. Contents are the same as posted
JACK1940383DAVID30284HAROLD68372TROY4392
The last example just parses the string data declared as a char[]
1. Using fscanf()
Since the target is to consume formatted data, fscanf() is an option. As the data structure is very simple, the program is just a one line loop:
char mask[] = "%50[^0-9]%50[0-9]";
while ( 2 == fscanf(F, mask, tk_key, tk_value))
std::cout << tk_key << "/" << tk_value << "\n";
program output
output is the same for all examples
JACK/1940383
DAVID/30284
HAROLD/68372
TROY/4392
code for ex. 1
#include <errno.h>
#include <iostream>
int main(int argc,char** argv)
{
if (argc < 2)
{ std::cerr << "Use: pgm FileName\n";
return -1;
}
FILE* F = fopen(argv[1], "r");
if (F == NULL)
{
perror("Could not open file");
return -1;
}
std::cerr << "File: \"" << argv[1] << "\"\n";
char tk_key[50], tk_value[50];
char mask[] = "%50[^0-9]%50[0-9]";
while ( 2 == fscanf(F, mask, tk_key, tk_value))
std::cout << tk_key << "/" << tk_value << "\n";
fclose(F);
return 0;
}
using a state machine
There are just 2 states so it is not a fancy FSA ;) State machines are good for representing this kind of stuff, albeit here this seems to be overkill.
#define S_LETTER 0
#define S_DIGIT 1
#include <algorithm>
#include <iostream>
#include <fstream>
using iich = std::istream_iterator<char>;
int main(int argc,char** argv)
{
std::ifstream in_file{argv[1]};
if ( not in_file.good()) return -1;
iich p {in_file}, eofile{};
std::string token{}; // string to build values
char st = S_LETTER; // state value for FSA
std::for_each(p, eofile,
[&token,&st](char ch)
{
char temp = 0;
switch (st)
{
case S_LETTER:
if ((ch >= '0') && (ch <= '9'))
{
std::cout << token << "/";
token = ch;
st = S_DIGIT; // now in number
}
else token += ch; // concat in string
break;
case S_DIGIT:
default:
if ((ch < '0') || (ch > '9'))
{ // is a letter
std::cout << token << "\n";
token = ch;
st = S_LETTER; // now in name
}
else token += ch; // concat in string
break;
}; // switch()
});
std::cout << token << "\n"; // print last token
}
Here we have no loop. for_each gets the data from an iterator and passes it to a function that builds the name and the value as strings and couts them
Output is the same
3. a simple FSA to consume the data
#define S_LETTER 0
#define S_DIGIT 1
#include <iostream>
int main(void)
{
char one[] = "JACK1940383DAVID30284HAROLD68372TROY4392";
char* p = (char*)&one;
char* token = p;
char st = S_LETTER;
char temp = 0;
while (*p != 0)
{
switch (st)
{
case S_LETTER:
if ((*p >= '0') && (*p <= '9'))
{
temp = *p;
*p = 0;
std::cout << token << "/";
*p = temp;
token = p;
st = S_DIGIT; // now in number
}
break;
case S_DIGIT:
default:
if ( (*p < '0') || (*p > '9'))
{ // letter
temp = *p;
*p = 0;
std::cout << token << "\n";
*p = temp;
token = p;
st = S_LETTER; // now in name
}
break;
}; // switch()
p += 1; // next symbol
}; // while()
std::cout << token << "\n"; // print last token
}
This code just uses a C-style loop to parse the input data
I've been working on an assignment to implement hashing. In it, I read through a text file called "proteins". The problem occurs when I try to copy it to another char array. Visual Studio throws a read access violation.
#include <iostream>
#include <fstream>
using namespace std;
struct arrayelement {
char protein[30];
int count;
};
arrayelement proteins[40];
int main()
{
char buffer[30];
// open source file
ifstream fin("proteins.txt");
if (!fin) { cerr << "Input file could not be opened\n"; exit(1); }
// loop through strings in file
while (fin >> buffer) {
int index = ((buffer[0] - 65) + (2 * (buffer[strlen(buffer)-1] - 65)) % 40);
while (true)
{
if (proteins[index].protein == buffer) // Found
{
proteins[index].count++;
break;
}
if (proteins[index].protein[0] == 0) // Empty
{
strcpy(proteins[index].protein, buffer); // <-- The error in question
proteins[index].count++;
break;
}
index++; // Collision
}
}
// close file
fin.close();
for (int i = 0; i <= 40; i++)
{
cout << proteins[i].protein << "\t" << proteins[i].count << "\n";
}
}
If you get more than 30 chars here:
while (fin >> buffer) {
... or if index >= 40 here:
strcpy(proteins[index].protein, buffer);
... the program will probably crash (Undefined behavior). Also, these char*'s will not be pointing at the same address, so the comparison will fail:
proteins[index].protein == buffer
I tried to make a program that loads chunks of a large (We're speaking of a few MBs) of file, and searches for a value, and prints its address and value, except my program every few times throws a !myfile , doesn't give the value except a weird symbol (Although I've used 'hex' in cout), the addresses seem to loop sorta, and it doesn't seem to find all the values at all. I've tried for a long time and I gave up, so I'm asking experiences coders out there to find the issue.
I should note that I'm trying to find a 32 bit value in this file, but all I could make was a program that checks bytes, i'd require assistance for that too.
#include <iostream>
#include <fstream>
#include <climits>
#include <sstream>
#include <windows.h>
#include <math.h>
using namespace std;
int get_file_size(std::string filename) // path to file
{
FILE *p_file = NULL;
p_file = fopen(filename.c_str(),"rb");
fseek(p_file,0,SEEK_END);
int size = ftell(p_file);
fclose(p_file);
return size;
}
int main( void )
{
ifstream myfile;
myfile.open( "file.bin", ios::binary | ios::in );
char addr_start = 0,
addr_end = 0,
temp2 = 0x40000;
bool found = false;
cout << "\nEnter address start (Little endian, hex): ";
cin >> hex >> addr_start;
cout << "\nEnter address end (Little endian, hex): ";
cin >> hex >> addr_end;
unsigned long int fsize = get_file_size("file.bin");
char buffer[100];
for(int counter = fsize; counter != 0; counter--)
{
myfile.read(buffer,100);
if(!myfile)
{
cout << "\nAn error has occurred. Bytes read: " << myfile.gcount();
myfile.clear();
}
for(int x = 0; x < 100 - 1 ; x++)
{
if(buffer[x] >= addr_start && buffer[x] <= addr_end)
cout << "Addr: " << (fsize - counter * x) << " Value: " << hex << buffer[x] << endl;
}
}
myfile.close();
system("PAUSE"); //Don't worry about its inefficiency
}
A simple program to search for a 32-bit integer in a binary file:
int main(void)
{
ifstream data_file("my_file.bin", ios::binary);
if (!data_file)
{
cerr << "Error opening my_file.bin.\n";
EXIT_FAILURE;
}
const uint32_t search_key = 0x12345678U;
uint32_t value;
while (data_file.read((char *) &value, sizeof(value))
{
if (value == search_key)
{
cout << "Found value.\n";
break;
}
}
return EXIT_SUCCESS;
}
You could augment the performance by reading into a buffer and searching the buffer.
//...
const unsigned int BUFFER_SIZE = 1024;
static uint32_t buffer[BUFFER_SIZE];
while (data_file.read((char *)&(buffer[0]), sizeof(buffer) / sizeof(uint32_t))
{
int bytes_read = data_file.gcount();
if (bytes_read > 0)
{
values_read = ((unsigned int) bytes_read) / sizeof(uint32_t);
for (unsigned int index = 0U; index < values_read; ++index)
{
if (buffer[index] == search_key)
{
cout << "Value found.\n";
break;
}
}
}
}
With the above code, when the read fails, the number of bytes should be checked, and if any bytes were read, the buffer then searched.
I need help loading a custom file format into my program made in c++...
I know there's a simple way of doing this but I think I'm using the
wrong terms to search for it online...
My custom format for 3d objects is as follows:
NumVerts 6
//verts (float)
-1
-1
0
1
-1
0
-1
1
0
1
-1
0
1
1
0
-1
1
0
//texture (float)
0
0
1
0
0
1
1
0
1
1
0
1
//index (int)
0
1
2
1
3
2
And that is a quad... (yeas; I know... horrible format... but it's what I'm using for an android game).
I want to make a function in c++ for my editor (SDL + OpenGL for windows) that loads these files into data... Unfortunately though I know how to export this format with C++, I can't figure out how to import them... I wish to use the fstream commands...
If someone could quickly write out a simple version I'd be really thankful.
I just it to do the following:
Find text file from input string
read "NumVerts" and grab the integer written after it
loop through the next (NumVerts*3) lines and grab each number as a float
loop though the next (NumVerts*2) lines and grab each number as a float
loop through the next (NumVerts*1) lines and grab each number as an Int
(skip any line that starts with "//")
close file.
Thank you for reading and any help would be really good right now... or a relivent link that is quite simple and reads strings from files and grabs numbers from them to be placed into memory...
I really just want to finish this game and it's getting really stressful trying to locate helpful tutorials.
Update: updated the script... I accidently forgot to seperate 1's and 0's...
Hope this helps. Incidentally, you have the wrong number of vertex components - you need 18 of them.
#include <algorithm>
#include <exception>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <boost/algorithm/string/trim.hpp>
#include <boost/lexical_cast.hpp>
using boost::lexical_cast;
int load_3d_object(const std::string& filename,
std::vector<float>& vertComponents,
std::vector<float>& texComponents,
std::vector<int>& indices)
{
std::ifstream fs(filename.c_str());
std::string line;
if(!std::getline(fs, line))
{
throw std::runtime_error("The input file is empty");
}
if(line.substr(0,8) != "NumVerts")
{
throw std::runtime_error("The first line must start with NumVerts");
}
// Extract the number of vertices.
int numVerts = lexical_cast<int>(line.substr(line.find(' ') + 1));
// Read in the vertex components, texture components and indices.
while(std::getline(fs, line))
{
boost::trim(line);
if(line.substr(0,2) == "//") continue;
if((int)vertComponents.size() < numVerts * 3) vertComponents.push_back(lexical_cast<float>(line));
else if((int)texComponents.size() < numVerts * 2) texComponents.push_back(lexical_cast<float>(line));
else indices.push_back(lexical_cast<int>(line));
}
return numVerts;
}
int main()
{
std::vector<float> vertComponents;
std::vector<float> texComponents;
std::vector<int> indices;
try
{
int numVerts = load_3d_object("input.txt", vertComponents, texComponents, indices);
}
catch(std::exception& e)
{
std::cout << e.what() << '\n';
}
return 0;
}
Hopefully this might help (minimal error checking):
int _get_num_verts_value(std::ifstream& a_in)
{
char buf[128];
int result = -1;
a_in.getline(buf, sizeof(buf));
while (a_in.good())
{
if (a_in.gcount() > 9)
{
string s(buf);
if (0 == s.find("NumVerts "))
{
result = atoi(s.substr(8).c_str());
break;
}
}
a_in.getline(buf, sizeof(buf));
}
return result;
}
template <typename T>
void _get_values(std::ifstream& a_in, std::vector<T>& a_values)
{
char buf[128];
a_in.getline(buf, sizeof(buf));
int i = 0;
while (a_in.good())
{
string line(buf);
if (0 != line.find("//"))
{
a_values[i++] = boost::lexical_cast<T>(line);
// All read ?
if (i == a_values.capacity())
{
break;
}
}
a_in.getline(buf, sizeof(buf));
}
}
int main(int /*a_argc*/, char** /*a_argv*/)
{
ifstream in("test.txt");
const int num_verts_value = _get_num_verts_value(in);
std::vector<float> verts(num_verts_value * 3);
_get_values<float>(in, verts);
std::vector<float> textures(num_verts_value * 2);
_get_values<float>(in, textures);
std::vector<int> indexes(num_verts_value);
_get_values<int>(in, indexes);
in.close();
return 0;
}
using a flat file for this purpose could be a bad idea. it's pretty fragile. I prefer to a binary file or a self-described file (e.g xml).
std::ifstream in("in");
char buf[256];
std::string line;
int NumVerts;
in >> buf >> NumVerts;
std::vector<float> verts(NumVerts * 3);
std::getline(in, line);//read the rest of the line
std::getline(in, line);//skip the comment line
std::for_each(verts.begin(), verts.end(), [&in](float& par){
in >> par;
});
std::vector<float> texture(NumVerts * 2);
std::getline(in, line);//read the rest of the line
std::getline(in, line);//skip the comment line
std::for_each(texture.begin(), texture.end(), [&in](float& par){
in >> par;
});
std::vector<int> index(NumVerts);
std::getline(in, line);//read the rest of the line
std::getline(in, line);//skip the comment line
std::for_each(index.begin(), index.end(), [&in](int& par){
in >> par;
});
For the links on how to DIY:
Tutorial: http://www.cplusplus.com/doc/tutorial/files/
Reference: http://new.cplusplus.com/reference/iostream/fstream/
Example code:
#include <string>
#include <iostream> // needed for printing stuff out
#include <fstream> // Needed to read the file
#include <stdexcept>
#include <vector>
using namespace std;
struct Vertice {
double x,y,z; // 3 part stuff to read
double a,b; // 2 part stuff to read
int i; // 1 int thing to read
};
/// Reads a number ignoring comment lines (and any other line that isn't a number)
template <typename T>
T readNumber(istream& data) {
char lastChar;
while (data.good()) {
data >> lastChar; // Don't use peek as that won't skip whitespace
data.putback(lastChar);
if (( (lastChar >= '0') && (lastChar <= '9') ) || (lastChar == '-')) {
// If we're looking at a number read and return it
T result;
data >> result;
return result;
} else {
// If it's not part of a number .. assume it's a comment line and skip the whole line
string commentLine;
getline(data, commentLine);
// TODO: Maybe just skip '//' lines and throw an exception for everything else..
}
}
throw exception("Couldn't read file");
}
double readDouble(istream& data) { return readNumber<double>(data); }
int readInt(istream& data) { return readNumber<int>(data); }
int main(int argc, char** argv) {
if (argc != 2)
cout << "Usage: " << argv[0] << " [DATA_FILE_NAME]" << endl;
else {
fstream data(argv[1], ios_base::in);
data >> skipws; // Skip whitespace
string lastString;
long numVerts = -1;
// Read in words ignoring everything until we hit a 'NumVerts'
while (numVerts < 0) {
data >> lastString;
if (lastString == "NumVerts")
data >> numVerts;
}
// We know how many to get now
typedef vector<Vertice> Verts;
Verts verts(numVerts);
// Read in the triples
for (Verts::iterator i=verts.begin(); i != verts.end(); ++i) {
i->x = readDouble(data);
i->y = readDouble(data);
i->z = readDouble(data);
}
// Read in the pairs (some other data)
for (Verts::iterator i=verts.begin(); i != verts.end(); ++i) {
i->a = readDouble(data);
i->b = readDouble(data);
}
// Read in the single integer value
for (Verts::iterator i=verts.begin(); i != verts.end(); ++i) {
i->i = readInt(data);
}
// Print out all we found
for (Verts::iterator i=verts.begin(); i != verts.end(); ++i) {
cout << "Vertice" << endl
<< " x: " << i->x << endl
<< " y: " << i->y << endl
<< " z: " << i->z << endl
<< " a: " << i->a << endl
<< " b: " << i->b << endl
<< " i: " << i->i << endl
<< endl;
}
}
}
The example code throws an error on the provided files as there isn't enough data to fill out the 'NumVerts'.
For my implementation of tail shell command in Linux, I need to read certain amount of lines/bytes from the end of the file using stream input/output. Does anyone have suggestions how to do that? I suspect I need to open a file and pass some parameter to the ifstream constructor, but I don't know what exactly. Googling didn't find anything.
Since tail needs to work with pipes, that you can't rewind, you'll have to keep a rotating buffer of the last n lines you've read which you will dump on EOF.
This problem is analogous to the problem of getting the last n nodes of a singly-linked list. You have to go all the way to the end with a buffer of n lines, then spit out the lines from buffer.
I don't think there's an easy way to go about this, you'll probably need to seek to the end of the file, back up one 'chunk' (an arbitrary size, but a couple of kilobytes perhaps), read that 'chunk' of data and start looking through it for new line characters, if you didn't find enough, you back up twice your chunk size (remember, you read forward, so you need to back up the one you read, plus the one you want to read next), and read in another one.
HTH
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
int main()
{
ifstream is("file.txt", ios::binary);
if (!is) {
cout << "Failed to open file" << endl;
return 1;
}
is.seekg(0, ios::end);
int len = is.tellg();
char c;
int n = 0;
ostringstream line;
int lines = 0;
for (int i = len - 1; i >= 0; --i) {
is.seekg(i, ios::beg);
is.get(c);
if (c == '\n' || i == 0) {
if (i < len - 1) {
if (i == 0) {
line << c;
}
string s = line.str();
cout << lines << ": " << string(s.rend() - n, s.rend()) << endl;
++lines;
n = 0;
line.seekp(0, ios::beg);
}
} else {
line << c;
++n;
}
}
is.close();
return 0;
}
this shows how you'd do it in c++... read successive chunks from the end of the file, then scan the chunks for new lines. if a newline isn't found, part of the chunk has to be kept around and combined with the next chunk read in...
//
// USAGE: lastln COUNT [FILE]
//
// Print at most COUNT lines from the end of FILE or standard input.
// If COUNT is -1, all lines are printed.
//
#include <errno.h>
#include <libgen.h>
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
int main(int argc, char **argv)
{
int ret = 0, maxLines = -1, len, count = 0, sz = 4096, lines = 0, rd;
istream *is;
ifstream ifs;
stringstream ss;
char *buf = NULL;
const char *prog = (argc > 0 && argv[0] ? basename(argv[0]) : "");
string line;
if (argc > 1) {
if ((maxLines = atoi(argv[1])) == 0) {
goto end;
}
}
if (argc > 2 && !(argv[2] && argv[2][0] == '-' && argv[2][1] == '\0')) {
ifs.open(argv[2], ios::in | ios::binary);
if (!ifs) {
ret = 1;
cerr << prog << ": " << argv[2] << ": " << strerror(errno) << endl;
goto end;
}
is = &ifs;
} else {
ss << cin.rdbuf();
if (!ss) {
ret = 1;
cerr << prog << ": failed to read input" << endl;
goto end;
}
is = &ss;
}
is->seekg(0, ios::end);
len = is->tellg();
buf = new char[sz + 1];
while (rd = min(len - count, sz)) {
is->seekg(0 - count - rd, ios::end);
is->read(buf, rd);
count += rd;
char *p = buf + rd, *q;
*p = '\0';
for (;;) {
q = (char *)memrchr(buf, '\n', p - buf);
if (q || count == len) {
if (q) *q = '\0';
if (lines || p - q - 1 > 0 || !q) {
++lines;
cout << lines << ": " << (q ? q + 1 : buf) << line << endl;
line.clear();
if (lines >= maxLines && maxLines != -1) break;
}
if (q) p = q; else break;
} else {
line = string(buf, p - buf) + line;
break;
}
}
}
end:
if (buf) delete[] buf;
return ret;
}