Read access violation when attempting strcpy from file buffer to char array - c++

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

Related

Stack Overflow with tail recursion function c++

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

c++ ifstream , Reading from file crashes

At the beginning I apologize for my English.
I was trying to write a XML Parser that I encountered a weird problem.
to explain my problem I should say, I have a xml parser class that has an ifstream member. And this class has a function which reads until it reaches an open tag matching with the given input.
this is the parser class I was working on:
// XMLParser.cpp
#include <fstream>
#include "Stack.h"
using namespace std;
class XMLParser{
private:
int charReadRate = 3;
public:
ifstream *stream;
XMLParser(string add){
stream = new ifstream(add); // open input stream
}
void nextTag(string tag){
// find the first occurance of open-tag with name 'tag'
cout << "nextTag\n";
char * readData;
string tagName="";
stream->read(readData, charReadRate);
int len = string(readData).length();
int i = 0;
// cout << len << endl;
while(true){
if((*readData) == '<'){
readData++;
i++;
while(*readData != '>'){
tagName+=*readData;
readData++;
i++;
if(i>=len){
if(stream->eof()){
return ; // error didn't find
}
stream->read(readData, charReadRate);
// cout << readData << endl;
len = string(readData).length();
i = 0;
}else{
if(tagName == tag){
// cout << "find\n";
stream->seekg(i-len, ios::cur);
return;
}
}
}
}else{
readData++;
i++;
if(i>=len){
if(stream->eof()){
return ; // error didn't find
}
stream->read(readData, charReadRate);
len = string(readData).length();
i = 0;
}
}
}
}
};
in the nextTag function I read the file until I reach the open tag which name's matches with the given input.
and here is my main function
int main(){
XMLParser parser("test.xml");
cout << "ready\n";
parser.nextTag("Log");
char *c;
parser.stream->read(c,3);
cout << c << endl;
return 0;
}
I have figured out that the program crashes when the fifth line of the main function [parser.stream->read(c,3);] is executed.
I wonder why this happens?
The char pointer you pass to ifstream::read is not initialized and thus points to an invalid memory region, causing your program to crash. You need it to point to a buffer you allocated:
int main(){
XMLParser parser("test.xml");
cout << "ready\n";
parser.nextTag("Log");
char c[3];
parser.stream->read(c,3);
cout << c << endl;
return 0;
}

Finding int value in large binary file c++

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.

Need help loading simple text data with c++

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'.

How do I read a given number of lines from the end of the file using streams in C++?

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;
}