01/10/2018, 08:27

Gặp lỗi terminate called after throwing an instance of 'std::bad_alloc' trong code nhập/xuất file

E đang làm bài tập trên trường như sau:

Đề bài: Cho danh sách các học sinh có trong lớp, biết thông tin 1 học sinh bao gồm: họ tên, số điện thoại, ngày sinh, điểm toán, lý hóa. Yêu cầu: Sắp xếp danh sách học sinh trong lớp giảm dần theo điểm trung bình biết công thức tính như sau:
Điểm trung bình = (toán + lý + hóa)/3
Dữ liệu đọc vào là từ file INPUT.TXT
Dữ liệu ghi ra là file OUTPUT.TXT

Còn đây là source code của e:

/* INPUT:
Barack Obama - +84978817165 - 68/6 Red Street - 9.75 8.5 9
Donald Trump - +84947821558 - 11 Green Street - 8.75 9.5 8.75
Hillary Clinton - +841299992930 - 22 Yellow Street - 9.25 8 9
Bill Clinton - +84988345692 - 69/6 Pink Street - 9 8.75 8.5
George Washington - +84982584394 - 24/5 Brown Street - 9.25 9 8.75
*/
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <algorithm>

typedef struct {
    std::string name, phone_number, address;
    double Math, Physics, Chemistry, AveragePoint;
} stdinf;

std::vector<stdinf> ReadFile(std::fstream &); // Read data from a file to a vector
void SortStudent(std::vector<stdinf> &); // Sort the vector by average point
void WriteFile(std::fstream &, std::vector<stdinf>); // Write the vector to a file

std::vector<stdinf> ReadFile(std::fstream &FileIn)
{
    std::vector<stdinf> s;
    FileIn.open("INPUT", std::ios::in);
    while (!FileIn.eof()) {
        std::string Name, Phone_number, Address;
        double math, physics, chemistry, averagePoint;
        std::getline(FileIn, Name, '-'); Name.erase(Name.end() - 1); FileIn.seekg(1, std::ios::cur);
        std::getline(FileIn, Phone_number, '-'); Phone_number.erase(Phone_number.end() - 1); FileIn.seekg(1, std::ios::cur);
        std::getline(FileIn, Address, '-'); Address.erase(Address.end() - 1); FileIn.seekg(1, std::ios::cur);
        FileIn >> math >> physics >> chemistry;
        averagePoint = (math + physics + chemistry) / 3;
        stdinf f;
        f.name = Name; f.phone_number = Phone_number; f.address = Address; f.Math = math; f.Physics = physics;
        f.Chemistry = chemistry; f.AveragePoint = averagePoint;
        s.push_back(f);
    }
    return s;
}

void SortStudent(std::vector<stdinf> &s)
{
    int length = s.size();
    for (int i = 0; i < length - 1; ++i) {
        for (int j = i + 1; j < length; ++j) {
            if (s[i].AveragePoint > s[j].AveragePoint)
                std::swap(s[i].AveragePoint, s[j].AveragePoint);
        }
    }
}

void WriteFile(std::fstream &FileOut, std::vector<stdinf> s)
{
    FileOut.open("OUTPUT", std::ios::out);
    int length = s.size();
    for (int i = 0; i < length; ++i) {
        FileOut << "Student " << i + 1 << ":
";
        FileOut << "Name: " << s[i].name << std::endl;
        FileOut << "Phone number: " << s[i].phone_number << std::endl;
        FileOut << "Address: " << s[i].address << std::endl;
        FileOut << "Math - Physics - Chemistry point: " << s[i].Math << " " << s[i].Physics << " " << s[i].Chemistry << std::endl;
    }
}

int main()
{
    std::fstream FileIn, FileOut;
    std::vector<stdinf> s;
    s = ReadFile(FileIn);
    SortStudent(s);
    WriteFile(FileOut, s);
    FileOut.close();
    FileIn.close();
    return 0;
}

Khi em compile và run lên bằng terminal thì nó lại bị lỗi như sau:

E kiểm tra lại code kỹ càng lắm rồi mà vẫn không hiểu sao lại bị lỗi đó, các pro giúp em với ạ !
Em cảm ơn trước

Long Dragon viết 10:29 ngày 01/10/2018

Up up up …

*grab popcorn* viết 10:36 ngày 01/10/2018

Check xem file đúng và đủ input chưa.

Long Dragon viết 10:34 ngày 01/10/2018

Đủ rồi mà anh, trước đó em có in ra thử toàn bộ data trong file INPUT thì bình thường, sau đó mới tiến hành code

Nguyen Minh Tuan viết 10:37 ngày 01/10/2018

if (s[i].AveragePoint > s[j].AveragePoint)
std::swap(s[i].AveragePoint, s[j].AveragePoint);
}

bạn đổi dấu xét lại trong vòng điều kiện và swap hai phần tử chứ không swap 2 biến.
Vậy mới đúng với yêu cầu bài của bạn.

*grab popcorn* viết 10:30 ngày 01/10/2018

:c Tại mình test input đúng nó chả báo lỗi như trên hình.

Long Dragon viết 10:42 ngày 01/10/2018

bạn đổi dấu xét lại trong vòng điều kiện và swap hai phần tử chứ không swap 2 biến.

À chỗ này e quên mất để std::swap(s[i], s[j]) mới đúng đúng không anh ?
Cứ mãi tập trung tìm bug trong code mà quên mất luôn cái đề bài

:c Tại mình test input đúng nó chả báo lỗi như trên hình.

Quải thế nhỉ :v
Em dùng g++ để build, không biết compiler của a có khác ko ?

*grab popcorn* viết 10:37 ngày 01/10/2018

Mình dùng g++ luôn. GNU v5.4.0.
Mà check lại xem file INPUT có trong cùng thư mục chay chương trình chưa (lưu ý là đúng tên, đúng hoa thường, ko có đuôi này nọ).

Long Dragon viết 10:29 ngày 01/10/2018

Đúng mà anh, có khi nào compiler của e nó dở chứng ko nhỉ

Hay là có khi nào do .seekg() không nhỉ

Long Dragon viết 10:33 ngày 01/10/2018

Update source code:

/* INPUT:
Barack Obama - +84978817165 - 68/6 Red Street - 9.75 8.5 9
Donald Trump - +84947821558 - 11 Green Street - 8.75 9.5 8.75
Hillary Clinton - +841299992930 - 22 Yellow Street - 9.25 8 9
Bill Clinton - +84988345692 - 69/6 Pink Street - 9 8.75 8.5
George Washington - +84982584394 - 24/5 Brown Street - 9.25 9 8.75
*/

#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <algorithm>

typedef struct {
	std::string name, phone_number, address;
	double Math, Physics, Chemistry, AveragePoint;
} stdinf;

std::vector<stdinf> ReadFile(std::fstream &); // Read data from a file to a vector
void SortStudent(std::vector<stdinf> &); // Sort the vector by average point
void WriteFile(std::fstream &, std::vector<stdinf>); // Write the vector to a file

std::vector<stdinf> ReadFile(std::fstream &FileIn)
{
	std::vector<stdinf> s;
	FileIn.open("INPUT.txt", std::ios::in);
	while (!FileIn.eof()) {
		std::string Name, Phone_number, Address;
		double math, physics, chemistry, averagePoint;
		std::getline(FileIn, Name, '-'); Name.erase(Name.end() - 1); FileIn.seekg(1, std::ios::cur);
		std::getline(FileIn, Phone_number, '-'); Phone_number.erase(Phone_number.end() - 1); FileIn.seekg(1, std::ios::cur);
		std::getline(FileIn, Address, '-'); Address.erase(Address.end() - 1); FileIn.seekg(1, std::ios::cur);
		FileIn >> math >> physics >> chemistry; FileIn.seekg(2, std::ios::cur);
		averagePoint = (math + physics + chemistry) / 3;
		stdinf f;
		f.name = Name; f.phone_number = Phone_number; f.address = Address; f.Math = math; f.Physics = physics;
		f.Chemistry = chemistry; f.AveragePoint = averagePoint;
		s.push_back(f);
	}
	return s;
}

void SortStudent(std::vector<stdinf> &s)
{
	int length = s.size();
	for (int i = 0; i < length - 1; ++i) {
		for (int j = i + 1; j < length; ++j) {
			if (s[i].AveragePoint < s[j].AveragePoint)
				std::swap(s[i], s[j]);
		}
	}
}

void WriteFile(std::fstream &FileOut, std::vector<stdinf> s)
{
	FileOut.open("OUTPUT.txt", std::ios::out);
	int length = s.size();
	for (int i = 0; i < length; ++i) {
		FileOut << "Student " << i + 1 << ":\n";
		FileOut << "Name: " << s[i].name << std::endl;
		FileOut << "Phone number: " << s[i].phone_number << std::endl;
		FileOut << "Address: " << s[i].address << std::endl;
		FileOut << "Math - Physics - Chemistry point: " << s[i].Math << " " << s[i].Physics << " " << s[i].Chemistry << std::endl;
	}
}

int main()
{
	std::fstream FileIn, FileOut;
	std::vector<stdinf> s;
	s = ReadFile(FileIn);
	SortStudent(s);
	WriteFile(FileOut, s);
	FileOut.close();
	FileIn.close();
	return 0;
}
Long Dragon viết 10:34 ngày 01/10/2018

@drgnz em có thử debug thì phát hiện nó sai ở chỗ vòng lặp: while (!FileIn.eof()) {...} .
Khi chương trình đọc xong dòng cuối cùng (George Washington) trong file thì tự nhiên nó vẫn thực hiện được tiếp vòng lặp ??

*grab popcorn* viết 10:40 ngày 01/10/2018

Chạy trên 6.3 lòi ra bug. Đúng là do hàm seekg sai.
Bạn chỉ cần bỏ đi hết là được.

Còn vì sao thì getline bản thân nó cũng đã đọc dấu “-” của bạn và nó bỏ qua rồi, nên ko cần seek sang 1 byte từ vị trí đang trỏ nữa

Long Dragon viết 10:35 ngày 01/10/2018

Đúng là do hàm seekg sai.

Nó sai vì lý do gì a ?

Còn vì sao thì getline bản thân nó cũng đã đọc dấu “-” của bạn và nó bỏ qua rồi, nên ko cần seek sang 1 byte từ vị trí đang trỏ nữa

E tính làm như thế để khỏi phải tốn công erase đó mà

Edit: Mà em xóa hết mấy dòng .seekg(); nó vẫn lỗi vậy anh ơi

Em nghĩ là do: while (!FileIn.eof()) ấy, vì hỗi nãy e debug thấy mặc dù tới cuối file rồi những vòng lặp vẫn tiếp tục thực hiện nên tới chỗ getline bị sai, nhưng e lại ko hiểu vì sao nó lại thực hiện tiếp mặc dù đã đến cuối file nữa

*grab popcorn* viết 10:39 ngày 01/10/2018

À rồi, eof() nó chỉ trả về true sau khi mà nó đọc hết filestream.
Vậy nên trường hợp trên, có thể data đã hết, nhưng filestream thì chưa -> Lỗi.
Thay bằng ntn xem fix được ko nhé :

std::string Name, Phone_number, Address;
while (std::getline(FileIn, Name, '-')) {
  ...
}

http://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong

rogp10 viết 10:28 ngày 01/10/2018

Đọc thấy nó sai sai rồi (while (!eof()) nhưng mà không ngờ lại sinh exception này mới lạ.

Long Dragon viết 10:35 ngày 01/10/2018

Vậy nên trường hợp trên, có thể data đã hết, nhưng filestream thì chưa -> Lỗi.

Em chưa hiểu chỗ này lắm, anh có thể nói kỹ hơn xíu dc ko ?

Đúng là do hàm seekg sai.

Anh trả lời giùm e câu này luôn nhé ^^ (vì sao nó wrong ấy)

Thay bằng ntn xem fix được ko nhé :

Đọc thấy nó sai sai rồi (while (!eof()) nhưng mà không ngờ lại sinh exception này mới lạ.

Yeah, nó chạy được rồi, thì ra đây là exception


Khi e mở file OUTPUT bằng sudo gedit ./OUTPUT thì nó vẫn hiện ra file OUTPUT bằng gedit nhưng trên terminal có mấy cái dòng warning này là sao nhể a @drgnz :

P/S: Mà hình như giữa chữ name với tên có \n thì phải:

E dùng seekg để xóa đi cái \n trước mỗi cái tên dc ko a @drgnz ? (vì a kêu seekg bị lỗi nên e ko dám dùng )
Eidt: À, mà seekg và seekp có dùng được cho file văn bản ko nhỉ ?

*grab popcorn* viết 10:43 ngày 01/10/2018

Anh trả lời giùm e câu này luôn nhé ^^ (vì sao nó wrong ấy)

Không phải seekg sai. Mà dùng cho file văn bản như vậy thg sẽ bị sai lệch do có nhiều ký tự sẽ ko hiển thị nhưng nó vẫn được đọc vào -> seek sai. Như ký tự xuống dòng, window nhận là \r\n, nhưng nhiều OS khác (Linux chẳng hạn) có khi chỉ nhận \n là đủ rồi. (Với có lẽ do cái GCC 6.3 trên Windows của mình bị bug hàm tellg dẫn tới việc seekg sai @_@)

Em chưa hiểu chỗ này lắm, anh có thể nói kỹ hơn xíu dc ko ?

Tới đây thì mình thực sự ko rành nữa rồi. Tuy nhiên đọc SO (link trên) với Ref thì nghĩ nó ntn (để chắc hơn bạn có thể hỏi mấy anh rành C++ hơn nhé ):

eof() nó check eofbit chưa được bật thì nó return false, còn lại thì true.
Còn khi nào eofbit được bật? Đó là khi có lỗi xãy ra trong quá trình đọc file cụ thể là đọc lố file.
Thì ở th trên khi đọc xong hết các dữ liệu. Tuy nhiên vẫn chưa lỗi gì xảy ra -> eof mới hiểu là oh, chưa có lỗi gì cả vậy là chưa hết file, đọc tiếp. Tuy nhiên khi đọc thì lúc này mới lỗi, eofbit được bật, nhưng đã quá muộn rồi.

terminal có mấy cái dòng warning

Nếu thấy vậy bạn có thể mở bằng gksudo gedit … (lệnh mở các ứng dụng GUI bằng quyền root, kdesudo cũng được, nhưng ubuntu có gksudo sẵn rồi)

rogp10 viết 10:27 ngày 01/10/2018

Windows là \r\n luôn bạn

Long Dragon viết 10:29 ngày 01/10/2018

do có nhiều ký tự sẽ ko hiển thị nhưng nó vẫn được đọc vào -> seek sai

Là sao anh ? Trrong file văn bản cũng có ký tự ẩn ạ ?
Btw, e có thử lấy đoạn code trên test trên Codeblocks, bỏ seekg vào 1 vài chỗ rồi lúc in ra trong file OUTPUT, nó thiếu tùm lum hết (nghĩa là con trỏ chỉ vị nó nhảy lung tung)
vậy câu đó cũng đồng nghĩa với việc trong file nhị phân ko có ký tự ẩn hả an h?

Còn khi nào eofbit được bật? Đó là khi có lỗi xãy ra trong quá trình đọc file cụ thể là đọc lố file.

Wow, e tưởng là eof nó check vị trí của con trỏ chỉ vị, nếu con trỏ chỉ vị đến cuối file thì nó return true, chứ ?

Thì ở th trên khi đọc xong hết các dữ liệu. Tuy nhiên vẫn chưa lỗi gì xảy ra -> eof mới hiểu là oh, chưa có lỗi gì cả vậy là chưa hết file, đọc tiếp. Tuy nhiên khi đọc thì lúc này mới lỗi, eofbit được bật, nhưng đã quá muộn rồi.

ừm, sao e thấy có một số code họ dùng eof: while (!File.eof()) vẫn an toàn vậy a ?

*grab popcorn* viết 10:29 ngày 01/10/2018

vậy câu đó cũng đồng nghĩa với việc trong file nhị phân ko có ký tự ẩn hả an h?

Các file text nó có các whitespace character nhiều cái bị ẩn đi mình ko thấy bằng mắt thg nên đọc hay seek thg dễ sai (vì nhiều kkhi mình thấy nó ko có gì nhưng nó lại sờ sờ ở đó). Với như nói ở trên, có vẻ như hàm tellg (xác định vị trí hiện tại của con trỏ trỏ trong file) bị bug trên windows (cụ thể là GCC do mingw biên dịch) nên dẫn tới việc nhảy lung tung @_@

ừm, sao e thấy có một số code họ dùng eof: while (!File.eof()) vẫn an toàn vậy a ?

Có thể trong đoạn code đọc, có 1 phần code khi đọc là luôn đảm bảo lố EOF?

Long Dragon viết 10:31 ngày 01/10/2018

Các file text nó có các whitespace character nhiều cái bị ẩn đi mình ko thấy bằng mắt thg nên đọc hay seek thg dễ sai (vì nhiều kkhi mình thấy nó ko có gì nhưng nó lại sờ sờ ở đó)

E chưa hiểu lắm !
VD: Trong file.txt, khi e cho con trỏ tới trước khoảng trắng, giữ Shift rồi nhấn phím mũi tên qua phải, nó sẽ bôi đen cái ký tự khoảng trắng đó. Vậy trong trường hợp trên (như anh nói), ký tự khoảng trắng đó ko dùng cách Shift + -> để xác định dc ạ ?

Với như nói ở trên, có vẻ như hàm tellg (xác định vị trí hiện tại của con trỏ trỏ trong file) bị bug trên windows (cụ thể là GCC do mingw biên dịch) nên dẫn tới việc nhảy lung tung @_@

hàm tellg bị bug là sao a ? Mà seekg liên qua j đến tellg nhỉ ?

Có thể trong đoạn code đọc, có 1 phần code khi đọc là luôn đảm bảo lố EOF?

VD e có đoạn code sau:

int main()
{
    std::fstream File("INPUT.txt", std::ios::in);
    int x;
    while (!File.eof()) {
        File >> x;
        std::cout << x << "   ";
    }
    File.close();
    return 0;
}

trong file INPUT.txt có:

1 2 3 4 5

thì nó vẫn in đủ ra màn hình 5 số …
vậy trường hợp này ko bị lỗi đó a, và còn nhiều trường hợp khác dùng .eof() vẫn ko bị lỗi

Bài liên quan
0