Close Menu
GeekBlog

    Subscribe to Updates

    Get the latest creative news from FooBar about art, design and business.

    What's Hot

    MakuluLinux’s New AI-OS Wants to Run Your Whole Desktop, Not Just Answer Questions

    August 1, 2026

    AI Tokens Got 98% Cheaper. Corporate AI Bills Are Exploding Anyway

    July 31, 2026

    Hackers Are Hijacking Hotel Wi-Fi to Steal Microsoft 365 Logins Without a Single Phishing Email

    July 30, 2026
    Facebook X (Twitter) Instagram Threads
    GeekBlog
    • Home
    • Mobile
    • Tech News
    • Blog
    • How-To Guides
    • AI & Software
    Facebook
    GeekBlog
    Home»Blog»How to Store Text File Data Into a Vector in C++
    Blog

    How to Store Text File Data Into a Vector in C++

    Ethan CaldwellBy Ethan CaldwellAugust 2, 2025Updated:July 30, 202615 Mins Read
    Share Facebook Twitter Pinterest LinkedIn Tumblr Email Copy Link
    Developer screen with C++ code that reads a text file into a vector of strings
    Share
    Facebook Twitter LinkedIn Pinterest Email Copy Link

    To read a text file into a vector in C++, open an std::ifstream, then loop with while (std::getline(in, line)) and push_back each line into a std::vector<std::string>. That is five lines, it handles files of any size, and it is the answer for 90 percent of cases.

    Quick answer: For lines, use while (std::getline(in, line)) v.push_back(line);. For whitespace-separated tokens, use while (in >> value) v.push_back(value);. Never write while (!in.eof()) — it gives you one bogus extra element every time. Always check that the file opened before you loop.

    Every program below was compiled with g++ -std=c++20 -Wall -Wextra -pedantic on GCC 13.3 and run, and every output block is real program output. The examples assume a file called words.txt containing four lines (alpha, bravo, charlie, delta) and a nums.txt containing 12 7 -3 45 on one line and 9 100 2 on the next.

    The canonical answer: getline into a vector of strings

    This is the one to memorize. std::getline returns the stream, and a stream converts to false once a read fails, so the loop ends exactly when the file ends.

    #include <fstream>
    #include <iostream>
    #include <string>
    #include <vector>
    
    int main() {
        std::ifstream in("words.txt");
        if (!in) {
            std::cerr << "cannot open words.txt\n";
            return 1;
        }
    
        std::vector<std::string> lines;
        std::string line;
        while (std::getline(in, line))
            lines.push_back(line);
    
        std::cout << lines.size() << " lines\n";
        for (std::size_t i = 0; i < lines.size(); ++i)
            std::cout << i << ": " << lines[i] << '\n';
    }
    4 lines
    0: alpha
    1: bravo
    2: charlie
    3: delta

    Two details worth noticing. The std::string line is declared outside the loop, which lets it reuse its heap buffer across iterations instead of allocating a fresh one every line. And the newline itself is consumed and discarded, so no element ends with \n. On a CRLF file read on Linux, however, each element will end with \r, which is covered below. The exact contract, including what happens when a line exceeds the string’s capacity, is on the cppreference std::getline page.

    Numbers: operator>> into a vector of int or double

    If the file is a stream of numbers and you do not care where the line breaks fall, operator>> is simpler than getline plus parsing. It skips all leading whitespace, including newlines, and stops on the first thing that is not a valid number.

    #include <fstream>
    #include <iostream>
    #include <vector>
    
    int main() {
        std::ifstream in("nums.txt");
        if (!in) return 1;
    
        std::vector<int> values;
        int n = 0;
        while (in >> n)
            values.push_back(n);
    
        if (!in.eof()) {
            std::cerr << "stopped early on bad input\n";
            return 1;
        }
    
        std::cout << values.size() << " numbers:";
        for (int v : values) std::cout << ' ' << v;
        std::cout << '\n';
    }

    That prints 7 numbers: 12 7 -3 45 9 100 2. The if (!in.eof()) check after the loop is the part people skip. Without it, a file containing 12 7 oops 45 silently gives you two values and no complaint. Checking eof() after the loop is correct; using it as the loop condition is not.

    The iterator idiom, and where it breaks

    You can build the vector directly from a pair of stream iterators. It is compact and expressive, and it is the idiom most likely to be misused.

    #include <fstream>
    #include <iostream>
    #include <iterator>
    #include <string>
    #include <vector>
    
    int main() {
        std::ifstream in("words.txt");
        if (!in) return 1;
    
        // Note the extra parentheses / braces: this reads WHITESPACE-SEPARATED
        // tokens, not lines. "hello world" would become two elements.
        std::vector<std::string> tokens{std::istream_iterator<std::string>{in},
                                        std::istream_iterator<std::string>{}};
    
        std::cout << tokens.size() << " tokens\n";
    
        std::ifstream in2("nums.txt");
        std::vector<int> nums{std::istream_iterator<int>{in2},
                              std::istream_iterator<int>{}};
        std::cout << nums.size() << " ints, first = " << nums.front() << '\n';
    }
    Heads up: istream_iterator<std::string> reads tokens, not lines. Our four-line file has four one-word lines, so it happens to give four elements. Feed it a file with sentences and you get one element per word. There is no line-based stream iterator in the standard library, which is exactly why the getline loop remains the default. The other trap is the most vexing parse: written with round parentheses instead of braces, std::vector<std::string> v(std::istream_iterator<std::string>(in), std::istream_iterator<std::string>()); declares a function, not a vector.

    std::copy with std::back_inserter

    Same source, different destination. std::back_inserter wraps a container in an output iterator that calls push_back. Use this form when the vector already exists, or when you want to append a second file to it.

    #include <algorithm>
    #include <fstream>
    #include <iostream>
    #include <iterator>
    #include <vector>
    
    int main() {
        std::ifstream in("nums.txt");
        if (!in) return 1;
    
        std::vector<int> values;
        values.reserve(64);
        std::copy(std::istream_iterator<int>{in}, std::istream_iterator<int>{},
                  std::back_inserter(values));
    
        std::cout << values.size() << " values, sum = ";
        long sum = 0;
        for (int v : values) sum += v;
        std::cout << sum << '\n';
    }

    Prints 7 values, sum = 172. The reserve(64) is the point of interest here. A vector grows by reallocating and copying, so a million push_back calls trigger roughly twenty reallocations. Reserving an estimate up front removes them. You do not need an exact number; an order of magnitude is enough.

    Read the whole file, then split it

    One read syscall path instead of many. buf << in.rdbuf() copies the entire stream buffer into a string in one go, which is the fastest simple way to slurp a file. You then split it yourself.

    Recommended for you:

    How Create Site Quickly and Efficiently: A Step-by-Step Guide
    Blog·Aug 2, 2025

    How Create Site Quickly and Efficiently: A Step-by-Step Guide

    #include <fstream>
    #include <iostream>
    #include <sstream>
    #include <string>
    #include <vector>
    
    int main() {
        std::ifstream in("words.txt", std::ios::binary);
        if (!in) return 1;
    
        // Slurp the whole file into one string, then split it.
        std::ostringstream buf;
        buf << in.rdbuf();
        std::string whole = buf.str();
        std::cout << "file is " << whole.size() << " bytes\n";
    
        std::vector<std::string> lines;
        std::size_t start = 0;
        while (start <= whole.size()) {
            std::size_t nl = whole.find('\n', start);
            if (nl == std::string::npos) {
                if (start < whole.size()) lines.push_back(whole.substr(start));
                break;
            }
            lines.push_back(whole.substr(start, nl - start));
            start = nl + 1;
        }
    
        std::cout << lines.size() << " lines, last = " << lines.back() << '\n';
    }

    This is the right shape when you need the raw bytes anyway, or when you want std::string_view slices into one buffer rather than a vector of separately allocated strings. It is the wrong shape for a 10 GB log, because the whole file has to fit in memory at once.

    CSV-ish lines into a vector of structs

    The realistic version of this task. Read each line with getline, wrap it in a std::stringstream, then use getline(ss, field, ',') to split on commas. Note that every getline result is checked, and the numeric conversions are wrapped in a try block, so one malformed row does not take the program down.

    #include <fstream>
    #include <iostream>
    #include <sstream>
    #include <string>
    #include <vector>
    
    struct Reading {
        std::string station;
        int         year{};
        double      inches{};
    };
    
    int main() {
        {
            std::ofstream seed("rainfall.csv");
            seed << "station,year,inches\n"
                 << "Seattle WA,2024,39.34\n"
                 << "Phoenix AZ,2024,7.22\n"
                 << "Mobile AL,2024,66.05\n";
        }
    
        std::ifstream in("rainfall.csv");
        if (!in) return 1;
    
        std::vector<Reading> rows;
        std::string line;
        std::getline(in, line);              // drop the header
    
        while (std::getline(in, line)) {
            if (line.empty()) continue;
            std::stringstream ss(line);
            Reading r;
            std::string year, inches;
    
            if (!std::getline(ss, r.station, ',')) continue;
            if (!std::getline(ss, year, ','))      continue;
            if (!std::getline(ss, inches, ','))    continue;
    
            try {
                r.year   = std::stoi(year);
                r.inches = std::stod(inches);
            } catch (const std::exception& e) {
                std::cerr << "skipping bad row: " << line << " (" << e.what() << ")\n";
                continue;
            }
            rows.push_back(r);
        }
    
        for (const Reading& r : rows)
            std::cout << r.station << " | " << r.year << " | " << r.inches << "\n";
        std::cout << rows.size() << " rows parsed\n";
    }
    Seattle WA | 2024 | 39.34
    Phoenix AZ | 2024 | 7.22
    Mobile AL | 2024 | 66.05
    3 rows parsed
    Tip: This handles simple comma-separated data only. Real CSV allows quoted fields containing commas and embedded newlines, and a hand-rolled splitter will corrupt those silently. If your data comes from a spreadsheet export, use a CSV library. If it comes from your own program, control both ends: writing the file is covered in writing a file in C++ with ofstream.

    Why while (!file.eof()) is wrong

    This is the single most common bug in file-reading code, and it deserves a demonstration rather than an assertion. eof() does not mean “the next read will fail”. It means “a previous read already hit the end”. So the loop always runs one extra time, performs a read that fails, and then stores whatever garbage the failed read left behind.

    #include <fstream>
    #include <iostream>
    #include <string>
    #include <vector>
    
    int main() {
        { std::ofstream f("three.txt"); f << "one\ntwo\nthree\n"; }
        { std::ofstream f("t.txt");     f << "10 20 30\n"; }
    
        // Bug 1: getline in an eof() loop appends one empty string.
        std::vector<std::string> lines_bad;
        std::ifstream a("three.txt");
        while (!a.eof()) {
            std::string line;
            std::getline(a, line);
            lines_bad.push_back(line);
        }
    
        // Bug 2: operator>> in an eof() loop duplicates the last value.
        std::vector<int> nums_bad;
        std::ifstream b("t.txt");
        int n = 0;
        while (!b.eof()) {
            b >> n;
            nums_bad.push_back(n);
        }
    
        // The fix for both: test the read itself.
        std::vector<std::string> lines_ok;
        std::ifstream c("three.txt");
        std::string line;
        while (std::getline(c, line)) lines_ok.push_back(line);
    
        std::vector<int> nums_ok;
        std::ifstream d("t.txt");
        while (d >> n) nums_ok.push_back(n);
    
        std::cout << "eof() + getline -> " << lines_bad.size() << " elements:";
        for (const auto& s : lines_bad) std::cout << " [" << s << ']';
        std::cout << "\ngetline in while -> " << lines_ok.size() << " elements:";
        for (const auto& s : lines_ok) std::cout << " [" << s << ']';
    
        std::cout << "\neof() + >>       ->";
        for (int v : nums_bad) std::cout << ' ' << v;
        std::cout << "\n>> in while      ->";
        for (int v : nums_ok) std::cout << ' ' << v;
        std::cout << '\n';
    }
    eof() + getline -> 4 elements: [one] [two] [three] []
    getline in while -> 3 elements: [one] [two] [three]
    eof() + >>       -> 10 20 30 30
    >> in while      -> 10 20 30

    There it is, both flavors. With getline you get a phantom empty line at the end. With operator>> you get the classic duplicated last value, because the push_back happens on a variable that still holds 30 from the previous iteration when the final extraction fails. Either way the vector is wrong, and the bug survives every test where you only check the first few elements.

    Making it robust: BOM, CRLF, blanks and comments

    Real files are messier than tutorial files. Three problems account for most surprises: a UTF-8 byte order mark on the very first line, carriage returns from a Windows-authored file, and blank or commented lines you did not plan for. This loader handles all three and reserves capacity based on file size.

    #include <fstream>
    #include <iostream>
    #include <stdexcept>
    #include <string>
    #include <vector>
    
    // Strip a UTF-8 byte order mark from the first line, if present.
    void strip_bom(std::string& s) {
        if (s.size() >= 3 &&
            static_cast<unsigned char>(s[0]) == 0xEF &&
            static_cast<unsigned char>(s[1]) == 0xBB &&
            static_cast<unsigned char>(s[2]) == 0xBF)
            s.erase(0, 3);
    }
    
    // getline keeps the CR of a CRLF file when you read it on Linux or macOS.
    void strip_cr(std::string& s) {
        if (!s.empty() && s.back() == '\r') s.pop_back();
    }
    
    std::vector<std::string> load_lines(const std::string& path) {
        std::ifstream in(path, std::ios::binary);
        if (!in) throw std::runtime_error("cannot open " + path);
    
        // Size the vector up front so push_back does not keep reallocating.
        in.seekg(0, std::ios::end);
        const auto bytes = in.tellg();
        in.seekg(0, std::ios::beg);
    
        std::vector<std::string> out;
        if (bytes > 0) out.reserve(static_cast<std::size_t>(bytes) / 32 + 1);
    
        std::string line;
        bool first = true;
        while (std::getline(in, line)) {
            if (first) { strip_bom(line); first = false; }
            strip_cr(line);
            if (line.empty()) continue;                 // skip blank lines
            if (line[0] == '#' || line.rfind("//", 0) == 0) continue;  // skip comments
            out.push_back(line);
        }
        return out;
    }
    
    int main() {
        {
            // A file with a BOM, CRLF endings, a comment and a blank line.
            std::ofstream seed("messy.txt", std::ios::binary);
            seed << "\xEF\xBB\xBF# config file\r\n"
                 << "host=example.com\r\n"
                 << "\r\n"
                 << "// commented out\r\n"
                 << "port=8080\r\n";
        }
    
        try {
            auto lines = load_lines("messy.txt");
            std::cout << lines.size() << " useful lines\n";
            for (const auto& l : lines)
                std::cout << '[' << l << "] length " << l.size() << '\n';
        } catch (const std::exception& e) {
            std::cerr << e.what() << '\n';
            return 1;
        }
    }
    2 useful lines
    [host=example.com] length 16
    [port=8080] length 9

    The lengths are the proof. host=example.com is 16 characters, so no stray \r survived. Without strip_cr it would read 17, and any later comparison against the literal "example.com" would fail for reasons invisible in a debugger’s string display. That class of invisible-mismatch bug also shows up when you compare two strings while ignoring case, where a trailing byte quietly changes the answer.

    Which approach should you use?

    ApproachUnitWhitespaceSpeedReadability
    getline loopLinesPreserved inside the lineGoodBest. Obvious to every reader.
    operator>> loopTokensSkipped and discardedGoodBest for pure number files.
    istream_iterator bracesTokens onlySkipped and discardedSame as >>Terse, but hides the token-vs-line trap.
    std::copy + back_inserterTokensSkipped and discardedSame as >>Good when appending to an existing vector.
    Slurp with rdbuf(), then splitWhatever you split onFully under your controlFastest for small and medium filesMore code. Needs the file to fit in RAM.
    getline + stringstreamFields per linePreserved, so trim yourselfSlowest per lineThe only sane option for structured rows.

    Troubleshooting

    The vector is empty and no error printed. The open failed and you did not check it. Add if (!in) { std::cerr << "open failed\n"; return 1; } immediately after construction. An ifstream on a missing file is a perfectly valid object that simply produces nothing.

    File not found even though it is right there. Relative paths resolve against the process working directory, not the source file’s folder. Print std::filesystem::current_path() once. In Visual Studio the default is the project directory; in CLion and VS Code it depends on the run configuration.

    Recommended for you:

    How to Monetize a WordPress Blog in 2026: 8 Income Streams and What They Really Pay
    Blog·Aug 2, 2025

    How to Monetize a WordPress Blog in 2026: 8 Income Streams and What They Really Pay

    One element too many. You wrote while (!in.eof()). Replace it with the read itself as the condition. If the extra element is an empty string in the middle rather than at the end, the file genuinely has a blank line and you should skip empties explicitly.

    Comparisons fail on lines that look identical. Trailing \r from a CRLF file, or a UTF-8 BOM on line one. Print line.size() and compare it against the character count you expect. Neither byte is visible in most editors or debuggers.

    The program uses too much memory on a big file. A vector<std::string> of ten million short lines costs far more than the file, because each std::string carries its own bookkeeping and, past the small-string threshold, its own heap allocation. If you must hold it all, slurp the file into one string and store std::string_view slices. Better still, process line by line and never build the vector.

    Frequently asked questions

    How do I read a text file line by line into a vector in C++?

    Open an std::ifstream, declare a std::string outside the loop, then run while (std::getline(in, line)) v.push_back(line);. Check the stream with if (!in) first. This handles arbitrary line lengths and needs no size known in advance.

    Why does my vector have an extra empty element at the end?

    Your loop condition is !file.eof(). That flag is only set after a read has already failed, so the body runs once more than it should and stores the result of the failed read. Use the read itself as the condition instead.

    Can I read a file into a vector of ints directly?

    Yes. while (in >> n) v.push_back(n); reads whitespace-separated integers regardless of line breaks. Or construct in one expression with std::vector<int> v{std::istream_iterator<int>{in}, {}};. Check in.eof() afterwards to distinguish a clean finish from a parse failure.

    Should I call reserve() before reading a file into a vector?

    Yes if the file is large and you can estimate the count, for example from the file size in bytes divided by an average line length. It removes the repeated reallocate-and-copy cycles that push_back triggers as the vector grows. For files under a few thousand lines it makes no measurable difference.

    How do I remove the carriage return when reading a Windows file on Linux?

    After each getline, check the last character: if (!line.empty() && line.back() == '\r') line.pop_back();. Text mode only translates line endings on Windows, so a CRLF file read on Linux or macOS keeps the \r as part of the string.

    What is the fastest way to read a large text file into memory in C++?

    Slurp it with buffer << in.rdbuf() after opening in binary mode, then index into that single string with std::string_view instead of copying each line. For files larger than available RAM, stream them line by line and process as you go rather than storing everything.

    Wrapping up

    Use the getline loop. It is short, it is obviously correct to anyone reading it, and it handles the two cases people actually have: one record per line, or one line to be split into fields. Reach for operator>> when the file is nothing but numbers, and for rdbuf() slurping when you are chasing throughput on a file you know fits in memory.

    Whatever you pick, do two things: check that the file opened, and never use eof() as a loop condition. Those two habits eliminate most of the bugs in this area. If you are turning these rows into objects of your own class, giving that class a operator>> makes the read loop read like the built-in one — see how to overload an operator in C++ for the non-member stream form. And if a whiteboard version of this question comes up, walking an interviewer through why the eof loop is wrong is a strong signal, which is the kind of thing explaining your coding solution in an interview gets into.

    Share. Facebook Twitter Pinterest LinkedIn Tumblr Telegram Email Copy Link
    Previous ArticleHow Create Site Quickly and Efficiently: A Step-by-Step Guide
    Next Article Chandelier Exit for Swing Trading Explained: A Clear Guide to Maximizing Profits
    Ethan Caldwell

      Ethan Caldwell is GeekBlog's resident Apple specialist, covering the entire Apple ecosystem - iPhone, iPad, Mac, Apple Watch, AirPods and the software that ties them together. A longtime iOS user and gadget collector, Ethan tracks Cupertino's every move, breaking down Apple keynotes, A- and M-series chip benchmarks, iOS feature updates and the rumor mill into clear, practical takes that help readers decide whether the latest Apple hardware is worth the upgrade.

      Related Posts

      18 Mins Read

      New York vs Florida: Which State Is Better to Move To?

      15 Mins Read

      What State Is Best to Invest in Real Estate in 2026?

      15 Mins Read

      Texas vs California: Which State Is Better in 2026?

      17 Mins Read

      How to Explain Your Coding Solution in an Interview

      13 Mins Read

      Massachusetts vs Kentucky: Which State Is Better?

      12 Mins Read

      Washington vs Indiana: Which State Is Better to Live In?

      Top Posts

      Best Stores for Buying MP3 and Digital Music You Can Keep Forever (2026)

      August 2, 202518 Views

      How to Fix PS5 Controller Stick Drift (2026): 7 Working Methods

      July 10, 202611 Views

      Japan Skips Exams Until Age 10 and Teaches Character Instead. The Results Are Complicated.

      July 29, 20268 Views
      Stay In Touch
      • Facebook

      Subscribe to Updates

      Get the latest tech news from FooBar about tech, design and biz.

      Most Popular

      Best Stores for Buying MP3 and Digital Music You Can Keep Forever (2026)

      August 2, 2025915 Views

      Discord will require a face scan or ID for full access next month

      February 9, 2026770 Views

      Trade in your old phone and get up to $1,100 off a new iPhone 17 at AT&T – here’s how

      September 10, 2025382 Views
      Our Picks

      MakuluLinux’s New AI-OS Wants to Run Your Whole Desktop, Not Just Answer Questions

      August 1, 2026

      AI Tokens Got 98% Cheaper. Corporate AI Bills Are Exploding Anyway

      July 31, 2026

      Hackers Are Hijacking Hotel Wi-Fi to Steal Microsoft 365 Logins Without a Single Phishing Email

      July 30, 2026

      Subscribe to Updates

      Get the latest creative news from FooBar about art, design and business.

      Facebook
      • About Us
      • Contact us
      • Privacy Policy
      • Disclaimer
      • Terms and Conditions
      © 2026 GeekBlog

      Type above and press Enter to search. Press Esc to cancel.