Given a text file. It is necessary to rewrite its lines to another file. The order of the lines in the second file must be reversed with respect to the order of the lines in the specified file.

Here is my code: (What's wrong with it, the program crashes)

#include "stdafx.h" #include <iostream> #include <Windows.h> #include <wincon.h> #include <conio.h> #include <cstdio> using namespace std; int main() { SetConsoleOutputCP(1251); SetConsoleCP(1251); FILE *read, *copy; int count = 0; //для подсчСта ΠΊΠΎΠ»-Π²Π° строк Π² массивС int i = 0; //для обращСния Π² ячСйку массива char buf[300]; //для хранСния строки char **mas; //основной Ρ€Π°Π±ΠΎΡ‡ΠΈΠΉ массив if ((fopen_s(&read, "read.txt", "r")) != 0) { printf("НСвозмоТно ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚ΡŒ Ρ„Π°ΠΉΠ».\n"); return 0; } if ((fopen_s(&copy, "copy.txt", "w+")) != 0) { printf("НСвозмоТно ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚ΡŒ Ρ„Π°ΠΉΠ».\n"); return 0; } while (!feof(read)) { fgets(buf, 300, read); count++; } fclose(read); cout << count << endl; //---------------------------------------создаСм динамичСский массив ΠΈ ΠΊΠΎΠΏΠΈΡ€ΡƒΠ΅ΠΌ Π² Π½Π΅Π³ΠΎ строки---------------- mas = new char*[count]; if (fopen_s(&read, "read.txt", "r")) { while (!feof(read)) { fgets(buf, 300, read); mas[i] = new char[strlen(buf)+1]; strcpy_s(mas[i], 300, buf); i++; } fclose(read); } for (int i = 0; i<count; i++) cout << mas[i] << endl; //------------------------------ΠΈΠ· массива ΠΊΠΎΠΏΠΈΡ€ΡƒΠ΅ΠΌ Π² Π½ΠΎΠ²Ρ‹ΠΉ Ρ„Π°ΠΉΠ» Π² ΠΎΠ±Ρ€Π°Ρ‚Π½ΠΎΠΌ порядкС--------------------------- if (copy) { for (int i = count - 1; i >= 0; i--) { fputs(mas[i], copy); if (i == count - 1) fputs("\n", copy); } fclose(copy); } for (int i = 0; i<count; i++) delete[]mas[i]; delete[]mas; return 0; } 
  • one
    new is no longer C :) - Harry
  • for a start: are you sure that you allocate enough memory for the array? otherwise you have the number of lines entered for some reason from the keyboard, and the buffer size is strictly limited. and what does "crashes" mean? Is the code compiled? - Andrej Levkovitch
  • strcpy_s (mas [i], 300, buf); - on this line gives the error "the program has been terminated" and closes the console. - Vitaliy Fedunyak
  • I'm going to: are you sure that you allocated enough memory? Does your mas [i] exist at all? - Andrej Levkovitch
  • 2
    In the string strcpy_s(mas[i], 300, buf); The second parameter is incorrect. The size of the buffer allocated for mas[i] is not at all 300. Well, the fidelity of the i index itself is also in doubt. - VTT

3 answers 3

An option that does not require downloading the source file to memory.

The idea is that in fact we can move (see fseek ) before writing to the file in any place and write the next line there. Therefore, we will write the lines in the output file back to front, moving from its end to the beginning.

Let's measure the size of the source file. Let's read the first line of it. Step back in the output file from the size of the input file for the length of the read line and write it down. Then we repeat, stepping back from the position of the beginning of the last displayed line.

However, better look at the code itself -)

 #include <stdio.h> #include <stdlib.h> int main (int argc, char const *argv[]) { if ( 3 != argc ) { fprintf(stderr, "Usage: ./a.out in-file out-reverse-file\n"); return -1; } FILE *fp_in = fopen( argv[1], "r" ); if ( NULL == fp_in ) return -2; FILE *fp_out = fopen(argv[2], "w"); if ( NULL == fp_out ) return -2; if ( fseek( fp_in, 0, SEEK_END ) ) return -3; long int outpos = ftell( fp_in ); /*Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ Π² outpos находится Ρ€Π°Π·ΠΌΠ΅Ρ€ Ρ„Π°ΠΉΠ»Π°*/ if ( fseek( fp_in, 0, SEEK_SET ) ) return -3; /*здСсь Π±ΡƒΠ΄Π΅Ρ‚ Ρ€Π°Π·ΠΌΠ΅Ρ€ ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Π½Π½ΠΎΠΉ строчки*/ ssize_t line_size; /*сюда Π±ΡƒΠ΄Π΅Ρ‚ Π·Π°ΠΏΠΈΡΡ‹Π²Π°Ρ‚ΡŒΡΡ сама строка*/ char* line = NULL; /*здСсь Π±ΡƒΠ΄Π΅Ρ‚ Ρ€Π°Π·ΠΌΠ΅Ρ€ для line, динамичСски Π²Ρ‹Π΄Π΅Π»Π΅Π½Π½ΠΎΠΉ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠ΅ΠΉ getline (ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ line Ρƒ нас NULL)*/ size_t strcap; while ( ( line_size = getline( &line, &strcap, fp_in ) ) > 0) { outpos -= line_size; if ( fseek( fp_out, outpos, SEEK_SET ) != 0 || fputs( line, fp_out ) == EOF ) return -4; } /*Π½Π΅ Π·Π°Π±Ρ‹Π²Π°Π΅ΠΌ ΠΎΡΠ²ΠΎΠ±ΠΎΠ΄ΠΈΡ‚ΡŒ ΠΏΠ°ΠΌΡΡ‚ΡŒ, динамичСски Π²Ρ‹Π΄Π΅Π»Π΅Π½Π½ΠΎΠΉ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠ΅ΠΉ getline*/ free(line); return 0; } 

If your system does not have getline , then writing it will be in any sense a useful exercise.

  • What is this trolling? Maybe you will turn the code? Or was it conceived? : D - Gromov Anton
  • @GromovAnton, of course, conceived (for people whose computer has not repulsed a sense of humor yet -)) (but if you insist, you can turn it over) - avp
  • I just disassembled the code: I am happy that I apologize and at the same time thank you for such a concise solution (not taking into account the sight-spoiling exit, rewind'a, for which you won’t follow, whether it was completed successfully, and getline itself but, fortunately, this is not so problematic). I told you the truth about my case, but I didn’t fully understand how you specifically write down, and I didn’t think that it was possible to shift the file stream in advance, without writing anything back. This is simply brilliant. Once again, thank you: "opened" my eyes: D - Gromov Anton
 #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <string.h> int main(int argc, char **argv) { if (argc != 3) { fprintf(stderr, "Usage: %s <file in> <file out>\n", argv[0]); return 1; } FILE *fin, *fout; if ((fin = fopen(argv[1], "r")) == NULL) { perror(argv[1]); return 2; } if ((fout = fopen(argv[2], "w")) == NULL) { fclose(fin); perror(argv[2]); return 3; } size_t lines = 0; while (!feof(fin)) { int k = fgetc(fin); if (k == '\n') lines++; } char buffer[256]; char **a = malloc(sizeof(char *) * lines); assert(a); fseek(fin, 0L, SEEK_SET); size_t i = 0; while (!feof(fin)) { if (fgets(buffer, sizeof(buffer), fin) != NULL) { size_t len = strlen(buffer); a[i] = malloc(len + 1); assert(a[i]); memcpy(a[i], buffer, len + 1); i++; } } for (long i = lines - 1; i >= 0; i--) { fprintf(fout, "%s", a[i]); free(a[i]); } free(a); fclose(fin); fclose(fout); return 0; } 
  • one
    Checking the feof before reading is wrong. Read and check the result - while(fgets(...)) { ... , while((k = fgetc(f)) != EOF) ... etc. - avp

Prologue:

At @avp, the solution is much better, but if for some reason you need some more complicated mediation with the text, writing them, for example, in char** , you can see my solution.


There are no functions, all in one, so you can not figure it out.

Let's just write it on the ANSI standard :)

A few notes:

  • few comments, because it would be redundant: D;
  • I tried to validate everything carefully, so the code seems large;
  • preprocessor directives - at the end.

main itself:

 /*gcc -o main ...*/ /*./main input_file output_file*/ int main(int argc, char const *argv[]) { if ( 3 != argc ) return -1; if ( 0 != write_reverse_from( argv[1], argv[2] ) ) { fprintf(stderr,"Writing has been failed!\n"); return -2; } return 0; } 

All the most interesting is in write_reverse_from (tried to write as much as possible, in my understanding, "pure", so that you could understand):

 int write_reverse_from ( const char* file_in_name, const char* file_out_name ) { FILE* fp_input; FILE* fp_output; char** input_content; uint32_t lines_count; char** output_content; fp_input = fopen( file_in_name, "r" ); fp_output = fopen( file_out_name, "w" ); if ( NULL == fp_input ) return -1; if ( NULL == fp_output ) return -2; /*ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ количСство строк Π² Ρ„Π°ΠΉΠ»Π΅*/ lines_count = get_lines_count_from( fp_input ); if ( 0 == lines_count ) return -3; /*ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ ΠΊΠΎΠ½Ρ‚Π΅Π½Ρ‚ Π² Π²ΠΈΠ΄Π΅ массива строк*/ input_content = get_input_content( fp_input, lines_count ); /*Π·Π°ΠΊΡ€Ρ‹Π²Π°Π΅ΠΌ Ρ„Π°ΠΉΠ»ΠΎΠ²Ρ‹ΠΉ ΠΏΠΎΡ‚ΠΎΠΊ исходного Ρ„Π°ΠΉΠ»Π°. Π‘ΠΎΠ»ΡŒΡˆΠ΅ Π½Π΅ Π½ΡƒΠΆΠ΅Π½*/ fclose( fp_input ); if ( NULL == input_content ) { fclose( fp_output ); return -4; } /*ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ "ΠΏΠ΅Ρ€Π΅Π²Ρ‘Ρ€Π½ΡƒΡ‚Ρ‹ΠΉ" массив строк*/ output_content = get_reversed_content_from( input_content, lines_count ); if ( NULL == output_content ) { free_content( input_content, lines_count ); fclose( fp_output ); return -5; } /*Π·Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ Π² ΠΈΡ‚ΠΎΠ³ΠΎΠ²Ρ‹ΠΉ Ρ„Π°ΠΉΠ»*/ print_content_to( fp_output, output_content, lines_count ); /*динамичСскоС освобоТдСниС памяти content-ΠΎΠ²*/ free_content( input_content, lines_count ); free_content( output_content, lines_count ); fclose( fp_output ); return 0; } 

I am showing now the implementation in order.

The function get_lines_count_from just "goes over" in a standard way (not feof way, please note: D) through the file, "winding" a simple counter:

 static uint32_t get_lines_count_from ( FILE* opened_fp ) { uint32_t lines_count = 0L; char line[ MAX_LINE_SIZE ]; if ( NULL == opened_fp ) return 0; fseek( opened_fp, 0L, SEEK_SET ); /* Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ с feof Π»ΡƒΡ‡ΡˆΠ΅ Π½Π΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ */ while ( NULL != fgets( line, MAX_LINE_SIZE, opened_fp ) ) lines_count++; return lines_count; } 

Here is how I take the contents of the input file:

 static char** get_input_content ( FILE* opened_fp, uint32_t lines_count ) { char line[ MAX_LINE_SIZE ] = {0}; char** content; uint32_t alloc_counter = 0L; if ( NULL == opened_fp || 0L == lines_count ) return NULL; content = malloc( sizeof( char** ) * lines_count ); while( alloc_counter < lines_count ) { content[alloc_counter] = malloc( sizeof(char*) * MAX_LINE_SIZE ); alloc_counter++; } alloc_counter = 0L; fseek( opened_fp, 0L, SEEK_SET ); while( NULL != fgets( line, MAX_LINE_SIZE, opened_fp ) ) { strcpy( content[alloc_counter], line ); memset( line, 0, MAX_LINE_SIZE); alloc_counter++; } return content; } 

Here it is "turning":

 static char** get_reversed_content_from( char** content, uint32_t lines_count ) { uint32_t alloc_counter = 0L; char* last_content_line; char** reversed_content; if ( NULL == content || 0L == lines_count ) return NULL; last_content_line = content[ lines_count - 1UL ]; if ( NULL == strchr( last_content_line, '\n' ) ) /*Ссли Π² послСднСй строкС Π½Π΅Ρ‚ символа Π½ΠΎΠ²ΠΎΠΉ строки*/ { if ( MAX_LINE_SIZE - 1UL <= strlen( last_content_line ) ) return NULL; strcat( last_content_line, LINE_END ); /*символ NUL послС этого Π΄ΠΎΠ±Π°Π²Π»ΡΡ‚ΡŒ Π½Π΅ ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ ΠΏΡ€ΠΈ Ρ‡Ρ‚Π΅Π½ΠΈΠΈ Ρƒ нас ифнормация заносилась Π² Π·Π°Ρ€Π°Π½Π΅Π΅ ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Π½Π½Ρ‹ΠΉ нулями массив, ΠΈ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΏΠΎΡ‚ΠΎΠΌ Π±Ρ‹Π»ΠΎ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Ρ‰Π΅Π½ΠΈΠ΅ Π² Π΄ΠΈΠ½Π°ΠΌΠΈΡ‡Π΅ΡΠΊΡƒΡŽ ΠΏΠ°ΠΌΡΡ‚ΡŒ (Π½Π°ΠΉΠ΄ΠΈΡ‚Π΅ строчку с memset)*/ } reversed_content = malloc( sizeof( char** ) * lines_count ); while( alloc_counter < lines_count ) { reversed_content[alloc_counter] = malloc( sizeof(char*) * MAX_LINE_SIZE ); strcpy(reversed_content[alloc_counter], content[lines_count - 1U - alloc_counter]); alloc_counter++; } return reversed_content; } 

Output to the final file:

 static int print_content_to( FILE* opened_fp, char** content, uint32_t lines_count) { uint32_t lines_counter = 0; if ( NULL == opened_fp || NULL == content || 0L == lines_count ) return -1; while( lines_counter < lines_count ) { fprintf(opened_fp, "%s", content[lines_counter]); lines_counter++; } return 0; } 

The very release of variables of type char** :

 static void free_content( char** content, uint32_t lines_count) { uint32_t lines_counter = 0; if ( NULL == content || 0 == lines_count ) return; while ( lines_counter < lines_count ) { free(content[lines_counter]); lines_counter++; } free(content); } 

Well, the promised directives:

 #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #define MAX_LINE_SIZE ( 1024UL ) #ifdef _WIN32 #define LINE_END "\r\n" #else #define LINE_END "\n" #endif 
  • And why completely copy the source file into memory? - avp
  • @avp, how do you think I could have done differently? "Filled" file data into memory -> made changes -> filled in the designated file. In short, I did not think of ways. If you hint that when you read a single line of the source file, you can immediately enter it at the beginning of the final file, then you are mistaken, because every time this line will be overwritten, and as a result there will be only one line in the final file: the last line of the original file . If you know how to make it better, then please write :) - Gromov Anton
  • @avp, only now I understand what you mean. I removed the "rb" argument, I tested something with it, and I forgot :) In general, Windows and Linux don't care: they still read it as it should: D - Gromov Anton
  • No, I mean that it is not necessary to read all the lines in memory in order to make a file with them in reverse order. You can first see the size of the input file and immediately make the output of the same size (system call ftruncate ). Then we read the input file line by line and then we write the line to the output, shifting to the desired distance from its end (or the current position in it (this is how you feel more comfortable -))). - avp pm
  • @avp First, the system call is a non-cross-platform solution. Second, how are you going to read and output a string in 1 iteration? You try it yourself at your leisure to do so, and then you will return to my earlier words: if I write the line in the output file, and on the next. iterations I will try to β€œshove” something into this line (and we, in fact, need it, if we decided to go this way), the information will be overwritten , but not added to this beginning (I tried to do that, too and I inform you that for one approach in the form of "read a line - output a line" for such a task is not suitable :)) - Gromov Anton