You have a lot of shortcomings (from the second time you cannot normally allocate dynamic memory, do not close the open file stream, incorrectly implement the reading process itself, a compiler error occurs on the line with strncpy , and, most likely, that is why you have a question, do you not ?), and therefore fix your code is equivalent to the fact that I just write my own , but that's okay, we will solve your problem :)
IMO, I would like to receive a triple pointer, but you need an "array of arrays", so let's assume that we want to get the following structure:
{ {"14"},{"@"},{"12"},{"&"}, {"41"},{"^"},{"35"},{"*"}, {"18"},{"$"},{"25"},{"!"} }
I will be brief, because there will be a lot of code.
Preprocessor directives that are out of function are in front of you:
#include <stdlib.h> #include <string.h> #include <stdio.h> #define MAX_LINE_SIZE ( 128u ) #define PARAM_COUNT_PER_LINE ( 4u ) #if ( PARAM_COUNT_PER_LINE != 2 && PARAM_COUNT_PER_LINE != 4 ) #error Parameter count should be 2 or 4. #endif #define DELIMITER ";" #define DELIMITER_SYMB ';' #define DELIMITER_COUNT ( PARAM_COUNT_PER_LINE - 1u ) /*1 - int32_t, 2 - int64-t, other option causes error*/ #define FIRST_PARAM_TYPE_OPTION 1 /* ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΡΠΈΠΌΠ²ΠΎΠ»ΠΎΠ² Π΄Π»Ρ ΡΠΈΡΠ»ΠΎΠ²ΡΡ
Π»ΠΈΡΠ΅ΡΠ°Π»ΠΎΠ²*/ /* Π²Ρ Π½Π΅ ΡΡΠΎΡΠ½ΠΈΠ»ΠΈ, ΡΠΊΠΎΠ»ΡΠΊΠΎ Π±ΡΡΠ΅Ρ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π²ΠΌΠ΅ΡΠ°ΡΡ ΡΠΈΠΌΠ²ΠΎΠ»ΠΎΠ² Π΄Π»Ρ ΡΠΈΡΠ»Π°, ΠΏΠΎΡΡΠΎΠΌΡ ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ Π±ΡΠ΄Π΅Ρ int32_t */ /*Π Π°ΡΡΠΈΡΡΠ²Π°Π» ΡΠ°ΠΊ:*/ /*1 - Π½Π° ΠΊΠΎΠ½Π΅Ρ ΡΡΡΠΎΠΊΠΈ, 1 - Π½Π° Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΡΠΉ Π·Π½Π°ΠΊ ΠΌΠΈΠ½ΡΡΠ°, ΠΎΡΡΠ°Π»ΡΠ½ΡΠ΅ - ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΡΠΈΠΌΠ²ΠΎΠ»ΠΎΠ² Π² ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡΠ½ΠΎΠΌ ΡΠΈΡΠ»Π΅*/ #if ( FIRST_PARAM_TYPE_OPTION == 1 ) #define FIRST_PARAM_TYPE int32_t #define FIRST_PARAM_SIZE ( 12u ) /* ΡΠ°Π·ΠΌΠ΅Ρ Π΄Π»Ρ int32_t */ #elif ( FIRST_PARAM_TYPE_OPTION == 2 ) #define FIRST_PARAM_TYPE int64_t #define FIRST_PARAM_SIZE ( 22u ) /* ΡΠ°Π·ΠΌΠ΅Ρ Π΄Π»Ρ int64_t */ #else #error Unable to set first parameter size. #endif #define SECOND_PARAM_SIZE ( 2u ) /*Π½Π΅ Π·Π½Π°Ρ, ΡΠΊΠΎΠ»ΡΠΊΠΎ Π²Π°ΠΌ Π½ΡΠΆΠ½ΠΎ ΡΠΈΠΌΠ²ΠΎΠ»ΠΎΠ², Π½ΠΎ ΠΏΡΡΡΡ Π±ΡΠ΄Π΅Ρ ΠΎΠ΄ΠΈΠ½ + ΡΠΈΠΌΠ²ΠΎΠ» ΠΊΠΎΠ½ΡΠ° ΡΡΡΠΎΠΊΠΈ*/
To validate the file, I will use the get_lines_count function. With at least one "invalid" line, the function will return zero, in the rest - the number of "correct" lines read.
static size_t get_lines_count ( const char* filename ) { FILE* fp; size_t lines_count = 0u; char line[ MAX_LINE_SIZE ]; fp = fopen( filename, "r" ); if ( NULL == fp ) return 0u; while ( fgets( line, MAX_LINE_SIZE, fp ) != NULL ) { if ( 0 != is_valid(line) ) { fclose(fp); return 0u; } lines_count++; } fclose(fp); return lines_count; }
The key function here is is_valid . That is what is needed for proper validation of the string. If you can improve the implementation - "forward."
static int is_valid( char* line ) /*ΡΠΊΠΎΡΠ΅Π΅ Π²ΡΠ΅Π³ΠΎ, ΠΌΠΎΠΆΠ½ΠΎ ΠΈ ΡΡΡΠ΅ΠΊΡΠΈΠ²Π½Π΅ΠΉ*/ { size_t symb_counter = 0u; size_t match_symb_count = 0u; size_t line_length; if ( NULL == line ) return -1; line_length = strlen(line); /*Π΅ΡΠ»ΠΈ ΡΠΎΡΠΊΠ° Ρ Π·Π°ΠΏΡΡΠΎΠΉ Π² ΡΠ°ΠΌΠΎΠΌ Π½Π°ΡΠ°Π»Π΅ */ if ( DELIMITER_SYMB == line[0u] ) return -2; while ( symb_counter < line_length ) { /* Π΅ΡΠ»ΠΈ ΠΏΠΎΠΏΠ°Π»Π°ΡΡ ΡΠΎΡΠΊΠ° Ρ Π·Π°ΠΏΡΡΠΎΠΉ, ΠΈ Π·Π° Π½Π΅ΠΉ Π½Π΅ ΠΈΠ΄ΡΡ ΡΠ»Π΅Π΄ΡΡΡΠ°Ρ*/ if ( DELIMITER_SYMB == line[symb_counter] && DELIMITER_SYMB != line[symb_counter + 1u] ) match_symb_count++; symb_counter++; } if ( DELIMITER_COUNT != match_symb_count ) return -3; return 0; }
Suppose that all lines after this will be valid. How to implement reading? Use get_params_from , which assumes the file name and the number of lines in the file (as you might guess, get_lines_count will be used "externally") Here is my implementation:
char** get_params_from( const char* filename, size_t lines_count ) { FILE* fp; char** result; char tmp_buf[MAX_LINE_SIZE]; size_t all_param_counter = 0u; if ( 0u == lines_count ) return NULL; fp = fopen( filename, "r" ); if ( NULL == fp ) return NULL; result = ( char** ) malloc ( sizeof( char* ) * lines_count * PARAM_COUNT_PER_LINE ); while ( all_param_counter < lines_count * PARAM_COUNT_PER_LINE ) { result[all_param_counter] = ( char* ) malloc ( sizeof( char ) * FIRST_PARAM_SIZE ); result[all_param_counter + 1u] = ( char* ) malloc ( sizeof( char ) * SECOND_PARAM_SIZE ); all_param_counter += 2u; } all_param_counter = 0u; while ( fgets( tmp_buf, MAX_LINE_SIZE, fp ) != NULL || all_param_counter < lines_count * PARAM_COUNT_PER_LINE ) { tmp_buf[strcspn( tmp_buf, "\n" )] = '\0'; /*ΠΎΠ΄ΠΈΠ½ ΠΈΠ· ΠΊΠΎΡΠΎΡΠΊΠΈΡ
ΡΠΏΠΎΡΠΎΠ±ΠΎΠ² ΡΠ±ΡΠ°ΡΡ newline ΠΈΠ· ΠΊΠΎΠ½ΡΠ°*/ paramcpy( result[all_param_counter], strtok( tmp_buf , DELIMITER), FIRST_PARAM_SIZE ); paramcpy( result[all_param_counter + 1u], strtok( NULL , DELIMITER), SECOND_PARAM_SIZE ); #if ( PARAM_COUNT_PER_LINE == 4 ) paramcpy( result[all_param_counter + 2u], strtok( NULL , DELIMITER), FIRST_PARAM_SIZE ); paramcpy( result[all_param_counter + 3u], strtok( NULL , DELIMITER), SECOND_PARAM_SIZE ); #endif all_param_counter += PARAM_COUNT_PER_LINE; } fclose(fp); return result; }
A few comments on the function:
Look at this "piece":
result = ( char** ) malloc ( sizeof( char* ) * lines_count * PARAM_COUNT_PER_LINE ); while ( all_param_counter < lines_count * PARAM_COUNT_PER_LINE ) { result[all_param_counter] = ( char* ) malloc ( sizeof( char ) * FIRST_PARAM_SIZE ); result[all_param_counter + 1u] = ( char* ) malloc ( sizeof( char ) * SECOND_PARAM_SIZE ); all_param_counter += 2u; }
I did not know how long (by the number of characters) your elements should be, so I decided to implement dynamic memory allocation in exactly this way.
paramcpy need the paramcpy function not only to copy the potential element itself into the allocated memory, but also to validate it. Implementation:
static int paramcpy( char* parameter, char* token, size_t param_size ) { if ( NULL == token || strlen(token) > param_size - 1) { parameter[0] = '\0'; return -1; } strncpy( parameter, token, param_size ); return 0; }
Note that if an invalidation is left, we simply leave the element empty, but you can catch it in get_params_from (after all, paramcpy can also return -1). Again, most likely, it can be modified for the better, so do not hesitate. :)
Principle, here you are and got your items. There are only 2 questions left:
- How to free up memory?
- How beautiful to display something? :)
one
This , thank God, is made a little easier than highlighting:
void free_params_buf ( char** params_buffer, size_t lines_count ) { size_t all_params_counter = 0u; while ( all_params_counter < lines_count * PARAM_COUNT_PER_LINE ) { free(params_buffer[all_params_counter]); all_params_counter++; } free(params_buffer); }
2
Well, you can like this:
void print_params_buf( char** params_buffer , size_t lines_count ) { size_t lines_counter = 0u; if ( NULL == params_buffer || 0u == lines_count ) return; printf("{\n"); while ( lines_counter < lines_count * PARAM_COUNT_PER_LINE ) { printf(" {\"%s\"},{\"%s\"}", params_buffer[lines_counter], params_buffer[lines_counter + 1u] ); #if ( PARAM_COUNT_PER_LINE == 4 ) printf(",{\"%s\"},{\"%s\"}", params_buffer[lines_counter + 2u], params_buffer[lines_counter + 3u]); #endif puts(""); lines_counter += PARAM_COUNT_PER_LINE; } printf("}\n"); }
Rest
main used the following:
int main( void ) { char const* filename = "file.txt"; size_t file_lines_count = get_lines_count(filename); char** all_params_buffer = get_params_from( filename, file_lines_count ); print_params_buf( all_params_buffer, file_lines_count ); free_params_buf(all_params_buffer, file_lines_count); return 0; }
That's why I used get_lines_count "from the outside": if you call in the functions themselves, then there will be a lot of calls to the file: D
The module (you can do full-fledged modules with your questions) can also work not only with the format β% s;% s;% s;% sβ, but also with β% s;% sβ (change the value of PARAM_COUNT_PER_LINE to 2u and the file structure itself, of course: D)
To cast to an integer type every odd element (that is, your integer), you can use strtol and cast to the type given in preprocessing ( int32_t or int64_t ).
Suppose I want to get the 5th element in int32_t (this is a platform-independent type for 32-bit int). Then I do the following: FIRST_PARAM_TYPE variable = (FIRST_PARAM_TYPE) strtol(params_buffer[5u], NULL, 10);
After preprocessing, it will be like int32_t variable = ( int32_t ) strtol(params_buffer[5u], NULL, 10); (don't forget about #include <stdint.h> )
Output examples
For your file:
{ {"14"},{"@"},{"12"},{"&"} {"41"},{"^"},{"35"},{"*"} {"18"},{"$"},{"25"},{"!"} }
For a file like:
14;@ 12;& 41;^ 35;* 18;$ 25;!
And setting PARAM_COUNT_PER_LINE to 2u will be:
{ {"14"},{"@"} {"12"},{"&"} {"41"},{"^"} {"35"},{"*"} {"18"},{"$"} {"25"},{"!"} }
";"(semicolon), and instrtok()feed just a comma ... What do you want from it? And about the record ... Well, there you can only copy the lines into your array (viastrncpy(), memcpy(), etc.). And naturally, you need to allocate all the necessary memory forresult, and not just an array of pointers. - Vladimirfor(int i=0; i<=line_nums; ++i) result[i] = malloc(line_length * sizeof(char) + 1);your array:for(int i=0; i<=line_nums; ++i) result[i] = malloc(line_length * sizeof(char) + 1);. (I took into account that you have the number of lines "+1", although this "+1" was implied, apparently, for '\ 0'? - then it is not necessary in external memory allocation). And at the end of working with such an array, you must also free up first in the cycleresult[i], and then theresultitself. - Vladimirline_lengthlineline_length- you have to solve it yourself according to the requirements of the problem (in the given example, I would think that 2 (+1 on '\ 0') would be enough). - Vladimir