Number lines read from a text file

This program numbers the lines found in a text file. Write a program that reads text from a file and outputs each line to the screen and to another file preceded by a line number. Print the line number at the start of the line and right-adjusted in a field of three spaces. Follow the line number with a colon, then one space, then the text of the line. You should get a character at a time and write code to ignore leading blanks on each line. You may assume that the lines are short enough to fit within a line on the screen.

A somewhat harder version determines the number of spaces needed in the field for the line numbers by counting lines before processing the lines of the file. This version of the program should insert a new line after the last complete word that will fit within a 10-character line.(I changed it from 72-character line to 10-character line to make testing easier)

I solved the easier version of the exercise. It is the "somewhat harder version" that I am having trouble with. I'm not sure what the part about "determining the number of spaces needed in the field for the line numbers by counting lines before processing the lines of the file" is asking me to do, so I ignored that part.

The chapter of the book that this programming exercise is from doesn't teach the "getline" function. I must solve this problem using "get", "put", "putback", ">>", and "<<".

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
 #include <iostream>
#include <fstream>
#include <cstdlib>
#include <iomanip>
using namespace std;

void copy_file(ifstream& fin, ofstream& fout);
void number_lines(ifstream& fin_a, ifstream& fin_b, ofstream& fout);

int main()
{
	ifstream fin_a, fin_b;
	ofstream fout;

	fin_a.open("textfile1.txt");
	if (fin_a.fail())
	{
		cout << "Input file opening failed.\n";
		system("pause");
		exit(1);
	}
	fout.open("textfile2.txt");
	if (fout.fail())
	{
		cout << "Output file opening failed.\n";
		system("pause");
		exit(1);
	}
	copy_file(fin_a, fout);
	fin_a.close();
	fout.close();
	fin_a.open("textfile1.txt");
	if (fin_a.fail())
	{
		cout << "Input file opening failed.\n";
		system("pause");
		exit(1);
	}
	fin_b.open("textfile2.txt");
	if (fin_b.fail())
	{
		cout << "Input file opening failed.\n";
		system("pause");
		exit(1);
	}
	fout.open("textfile3.txt");
	if (fout.fail())
	{
		cout << "Output file opening failed.\n";
		system("pause");
		exit(1);
	}
	number_lines(fin_a, fin_b, fout);
	fin_a.close();
	fin_b.close();
	fout.close();
	system("pause");
	return 0;
}
void copy_file(ifstream& fin, ofstream& fout)
{
	char next;
	fin.get(next);
	while (!fin.eof())
	{
		fout << next;
		fin.get(next);
	}
}
void number_lines(ifstream& fin_a, ifstream& fin_b, ofstream& fout)
{
	int line_count = 1, char_count, char2_count, space_count, word_count;
	int chars_after_last_space = 0, last_chars_after_last_space = 0;
	char next_a, next_b;
	bool count_ends_on_space;
	const int SPACES_PER_LINE = 10;
	fin_a.get(next_a);
	fin_b.get(next_b);
	while ((!fin_a.eof()) || (!fin_b.eof()))
	{ 
		char_count = 0, char2_count = 0, space_count = 0, word_count = 0;
		if (next_a == ' ')
		{
			while (next_a == ' ')
				fin_a.get(next_a);
		}
		while ((char_count < (SPACES_PER_LINE - last_chars_after_last_space)) && (!fin_a.eof()))
		{
			if ((next_a == ' ') || (next_a == '\n'))
				chars_after_last_space = 0;
			else
				chars_after_last_space++;

			if (next_a != '\n')
				char_count++;
			if ((next_a == ' ') || (next_a == '\n'))
				space_count++;
			fin_a.get(next_a);
		}
 		if ((next_a == ' ') || (next_a == '\n'))
			chars_after_last_space = 0;
		if (next_a != ' ')
			count_ends_on_space = false;
		else
			count_ends_on_space = true;
		if (next_b == ' ')
		{
			while (next_b == ' ')
				fin_b.get(next_b);
		}
		if (count_ends_on_space)
		{
			fout << setw(3) << line_count << ": ";
			cout << setw(3) << line_count << ": ";
			line_count++;
			while ((word_count < space_count) && (!fin_b.eof()))
			{

				if (next_b == '\n')
				{
					fout << ' ';
					cout << ' ';
				}
				else
				{
					fout << next_b;
					cout << next_b;
				}
				if (isspace(next_b))
					word_count++;
				fin_b.get(next_b);
			}
		}
		else if (!count_ends_on_space)
		{
			fout << setw(3) << line_count << ": ";
			cout << setw(3) << line_count << ": ";
			line_count++;
			while ((char2_count < (SPACES_PER_LINE - chars_after_last_space)) && (!fin_b.eof()))
			{
				{
					fout << next_b;
					cout << next_b;
					char2_count++;
				}
				fin_b.get(next_b);
				if (next_b == '\n')

					fin_b.get(next_b);
			}
		}
		fout << endl;
		cout << endl;
		last_chars_after_last_space = chars_after_last_space;
	}
}


This is what I have written in the source file:

123456789T
I went there
Is that?
I want to
I who Thee

This is what I end up with in the target file:

1: 123456789T
2: I went
3: thereIs
4: that?I
5: want to I
6: who Th
7: ee

I think I got pretty close to solving it but I've been going over it for days and I can' t get any further. When a word is too long to fit on the end of a line it is sent to the next line, but with no space between it and the following word.

My solution seems convoluted to me. I think there is a simpler way. I just can't see it. The book doesn't teach anything about creating a copy of a file in this chapter, so I think that's where I went wrong.
Last edited on
> My solution seems convoluted to me. I think there is a simpler way.

Simple brute force (caveat: untested):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <iostream>
#include <string>
#include <cctype>
#include <vector>
#include <stdexcept>
#include <fstream>
#include <iomanip>

// invariant: max_line_sz is greater than the size of the longest word
std::vector<std::string> split_line( std::string line, std::size_t max_line_sz )
{
    while( !line.empty() && std::isspace( line.back() ) ) line.pop_back() ;
    if( line.size() <= max_line_sz ) return { line } ;

    std::vector<std::string> result ;

    while( line.size() > max_line_sz )
    {
        std::size_t pos = max_line_sz ;
        while( !std::isspace( line[pos] ) && pos > 0 ) --pos ;
        if( pos == 0 ) throw std::domain_error( "word size not less than max_line_sz" ) ;

        result.push_back( line.substr( 0, pos ) ) ;
        line = line.substr( pos + 1 ) ;
    }

    if( !line.empty() ) result.push_back( line ) ;
    return result ;
}

std::streamsize num_lines( std::ifstream fin )
{
    std::streamsize n = 0 ;
    std::string line ;
    while( std::getline( fin, line ) ) ++n ;
    return n ;
}

int width_to_print_line_number( std::ifstream fin )
{ return std::to_string( num_lines( std::move( fin ) ) ).size() ; }

bool split_copy_file( std::string file_name, std::size_t max_line_sz, std::ostream& ostm = std::cout )
{
    const int width = width_to_print_line_number( std::ifstream( file_name ) ) ;
    std::size_t line_number = 0 ;

    std::string line ;
    std::ifstream fin( file_name ) ;
    while( std::getline( fin, line ) )
    {
        ++line_number ;

        const auto line_segments = split_line( line, max_line_sz ) ;

        ostm << std::setw( width ) << line_number << ". | " << line_segments[0] << '\n' ; // first segment

        for( std::size_t i = 1 ; i < line_segments.size() ; ++i ) // other segments (without line number)
            ostm << std::setw( width + 4 ) << " | " << line_segments[i] << '\n' ; //

        ostm << std::setw( width + 3 ) << " |" << '\n' ; // if we want spaces between lines
    }

    return fin.eof() && ostm ;
}

int main()
{
    split_copy_file( __FILE__, 72 ) ;

    // this is a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long line.
}

http://coliru.stacked-crooked.com/a/6dfa765da308de07
I would choose a different approach.

I would create a fucntion the outputs the file to an ostream.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void ShowFile(string filename, ostream out);

In main you just call the function twice.

void main()
{
ShowFile("textfile1.txt", cout); // display on screen

ofstream dest("textfile2.txt");

ShowFile("textfile1.txt", dest); // copy to file
}

To read a line basically you read char by char until you reach the "\n"
At this point in the book, I haven't learned about strings or vectors or arrays.
At this point in the book, I haven't learned about strings or vectors or arrays.


No problem just replace string with char*.
The book doesn't teach anything about creating a copy of a file in this chapter, so I think that's where I went wrong.

This is impossible to solve without some sort of storage, but I think if I were going to go the file route I would do a little processing and store word lengths rather than clone the file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include <fstream>
#include <iomanip>
#include <iostream>
#include <stdexcept>
#include <cctype>

template <typename stream_type>
stream_type open(const char* file_name)                 // reducing code duplication
{
    stream_type stream(file_name);
    if (!stream.is_open())
    {
        std::cerr << "Unable to open \"" << file_name << "\"\n";
        throw std::runtime_error("Unable to open file");        // friendlier exit()
    }
    return stream;
}

void consume_whitespace(std::istream& in)
{
    // in >> std::ws ;                  // equivalent to:
    while (std::isspace(in.peek()))     // while next character is whitespace
        in.ignore();                    // discard it.
}

unsigned next_word_length(std::istream& in)
{
    consume_whitespace(in);

    char ch;
    unsigned length = 0;
    while (in.get(ch) && !std::isspace(ch))
        ++length;

    return length;
}

void copy_next_word(std::ostream&out, std::istream& in, bool output_space)
{
    consume_whitespace(in);

    if (output_space)
        out.put(' ');

    char ch;
    while (in.get(ch) && !std::isspace(ch))
        out.put(ch);
}

void output_word_lengths(std::ostream& out, std::istream& in)
{
    while (unsigned word_len = next_word_length(in))
        out << word_len << '\n';
}

int main()
{
    const char* fname = "textfile1.txt";
    const char* word_len_name = "wordlens.txt";
    unsigned num_field_width = 3;

    {
        // prep
        std::ifstream in = open<std::ifstream>(fname);
        std::ofstream out = open<std::ofstream>(word_len_name);
        output_word_lengths(out, in);
    }

    std::ifstream in = open<std::ifstream>(fname);
    std::ifstream lengths = open<std::ifstream>(word_len_name);
    std::ostream & out = std::cout;

    const unsigned desired_length = 10;
    unsigned line_length = 0;
    unsigned line = 1;

    unsigned word_length;
    while (lengths >> word_length)
    {
        unsigned length = word_length + (line_length > 0);

        if (length + line_length > desired_length)
        {
            out << '\n';
            line_length = 0;
            ++line;
        }

        if (line_length == 0)
            out << std::setw(num_field_width) << line << ": ";

        copy_next_word(out, in, line_length != 0);
        line_length += word_length + (line_length > 0);
    }
}
Topic archived. No new replies allowed.