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

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 ạ ?

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

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 :slight_smile:

Ý là nó GCC của mình bị bug gì đó trỏ file sai -> tellg ra sai vị trí con trỏ đang trỏ trong file-> seekg dựa theo vị trí con trỏ hện tại nên sai theo.

Gần như vậy :slight_smile: Và có cả trường hợp khi bạn ghi file với fỏmat unicode mà đọc với ascii cũng bị lỗi nữa @_@

Conf cái cuối thì xin nhờ pro c++ giúp đỡ v. Chứ mình cũng nói là tới đó là ko rành và chỉ đọc ref và SO nên nghĩ nó hoạt động như vậy thôi :joy:

3 Likes

thử thêm 1 dấu cách, hoặc 1 dấu khoảng trắng nào đó phía sau số 5 nữa là thấy lỗi ngay. Vì có dư 1 khoảng trắng, operator>> của istream ko đọc ký tự khoảng trắng này, nên vẫn chưa tới end-of-file. Lần đọc File >> x tiếp theo đọc giữa chừng thì hết file => lỗi

theo chuẩn bất kì file input nào cũng kết thúc bằng 1 dấu xuống dòng \n. Xài eof() sẽ hỏng ngay. Đọc trên trang nào mà code nào mà xài eof thì tránh xa trang đó ra.

cách đọc ko xài eof: C++ cho phép ép kiểu stream thành kiểu bool tự động, nên khi đọc hết hoặc gặp lỗi, stream trả về sẽ tự động hiểu là false

while (File >> x)
{
    std::cout << x << "   ";
}

cực kì đơn giản, chả phải nhớ thêm chữ nào cả.

3 Likes

Cái này thớt phải overload cho class mới dùng được :slight_smile:

1 Like

bỏ xài seekg(1) luôn, xài FileIn.ignore() là nó cũng tự động bỏ qua 1 ký tự. Tới cuối dòng thì ko cần biết là \r\n hay \n, xài FileIn.ignore(100, '\n'); là nó tự động bỏ qua 100 ký tự hoặc tới khi gặp \n thì dừng.

bỏ luôn mấy cái biến phụ đi, sao ko xài thẳng cái struct luôn

    for (stdinf f; ; s.push_back(f))
    {
        if (!std::getline(FileIn, f.name, '-')) break;
        FileIn.ignore();
        if (!std::getline(FileIn, f.phone_number, '-')) break;
        FileIn.ignore();
        if (!std::getline(FileIn, f.address, '-')) break;
        FileIn.ignore();
        if (!(FileIn >> f.Math >> f.Physics >> f.Chemistry)) break;
        FileIn.ignore(100, '\n');
        
        f.AveragePoint = (f.Math + f.Physics + f.Chemistry) / 3;
        f.name.erase(f.name.end() - 1);
        f.phone_number.erase(f.phone_number.end() - 1);
        f.address.erase(f.address.end() - 1);
    }

đặt break tùm lum hết cho chắc, ko thì đặt ở điều kiện đầu f.name cũng đủ rồi

để cho chắc ăn dữ liệu struct nào nằm trên dòng struct đó thì đọc 1 lần 1 dòng, rồi parse dòng đó vào struct, xài stringstream:

for (std::string line; std::getline(FileIn, line); s.push_back(parseStdinf(line)));

viết 1 hàm stdinf parseStdinf(const std::string& record) nữa là xong:

stdinf parseStdinf(const std::string& record)
{
    std::istringstream in(record); //#include <sstream>
    stdinf f;
    
    if (!std::getline(in, f.name, '-'))
        throw std::runtime_error("Invalid record: name"); //#include <stdexcept>
    in.ignore();
    if (!std::getline(in, f.phone_number, '-'))
        throw std::runtime_error("Invalid record: phone_number");
    in.ignore();
    if (!std::getline(in, f.address, '-'))
        throw std::runtime_error("Invalid record: address");
    in.ignore();
    if (!(in >> f.Math >> f.Physics >> f.Chemistry))
        throw std::runtime_error("Invalid record: Math/Physics/Chemistry");
    
    f.AveragePoint = (f.Math + f.Physics + f.Chemistry) / 3;
    f.name.erase(f.name.end() - 1);
    f.phone_number.erase(f.phone_number.end() - 1);
    f.address.erase(f.address.end() - 1);
    return f;
}
2 Likes

vừa mới gu gồ, lỗi là ở đây: https://stackoverflow.com/questions/27055771/using-seekg-in-text-mode

seekg ở trong libstdc++ (g++ hoặc MinGW) dùng cho file ko có flag ios::binary mà xài seekg là bị sai…

thêm flag binary (chữa cháy, ko phải là cách đúng)

FileIn.open("INPUT.txt", std::ios::in | std::ios::binary);

và thêm cái check eof sau khi đọc Name:

        std::getline(FileIn, Name, '-');
        if (FileIn.eof()) break;
  • đọc file chữ số thường dưới dạng binary là ko hợp lý. Xài istream::ignore() cho file dạng text
  • xài eof là ko hợp lý. Xài operator bool() của istream mới đúng kiểu C++
  • seekg(2) cho ký tự xuống dòng chỉ đúng trên Windows, xài ignore(100, ‘\n’) cho chắc.
1 Like

Kinh thật :joy:
Cho e hỏi những điều trên có đúng với tập tin trong C ko anh @tntxtnt ? Và solution như thế nào ?

http://www.cplusplus.com/reference/cstdio/fseek/

For streams open in binary mode, the new position is defined by adding offset to a reference position specified by origin.

For streams open in text mode, offset shall either be zero or a value returned by a previous call to ftell, and origin shall necessarily be SEEK_SET.

http://www.cplusplus.com/reference/cstdio/ftell/

For binary streams, this is the number of bytes from the beginning of the file.

For text streams, the numerical value may not be meaningful but can still be used to restore the position to the same position later using fseek (if there are characters put back using ungetc still pending of being read, the behavior is undefined).

thấy nó ghi vậy nghĩa là chỉ bảo đảm cho binary mode, còn text mode thì ko xài vị trí hiện tại SEEK_CUR mà phải xài vị trí ban đầu SEEK_SET và offset lấy từ ftell @_@

1 Like
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?