-3

Can anyone explain to me the easiest way to create dynamically a 2D string array with stable second dimension? I've got a txt file with some strings in it and I want to transfer this txt to an array. So I want to associate the txt line number with the first dimension and the string itself with the second dimension. The second dimension is the number of characters in every row (which is stable because every line in the txt has a certain syntax) So if I have in my txt:

hello how (newline)
are youuu

*(I wrote youuu because as I said, every line has the same number of characters).

I want something like:

array[0]["hello how"],
array[1]["are youuu"]
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
Georgio3
  • 23
  • 7
  • Look at http://stackoverflow.com/questions/3536153/c-dynamically-growing-array – Jay Jan 10 '17 at 19:57
  • 2
    Are you just wondering how to dynamically allocate a 2D array, or how to resize one? Something like, `char (*arr)[CHARS_PER_LINE] = malloc(sizeof(*arr) * NUM_LINES);` maybe? – Dmitri Jan 10 '17 at 20:04
  • In your example, first line has a "(newline)", but the second one does not. Is that intentional, an oversight or what? – chux - Reinstate Monica Jan 10 '17 at 20:32
  • "second dimension is the number of characters" and "String Array" slightly contradicts. To store a _string_ in C, the size of the array needs to be "number of characters" + 1. – chux - Reinstate Monica Jan 10 '17 at 20:37

4 Answers4

2

Non numerical keys are not allowed in C. You're trying to do some PHP and JavaScript nonsense in a language that only works with numbers.

But, with C there is always 2 roads to hell.

char *lookup_key(int index, char *key) { ... }


printf(lookup_key(0, "hello how"));
Dellowar
  • 3,160
  • 1
  • 18
  • 37
2

If you know the length of the strings and how many you have, you can configure the array like this

char strings[numLines][strLen+1];

you can then access the array like this

strcpy(strings[1], "test2");

If you don't know anything beforehand, you need a pointer to pointer array and then use malloc to allocate space as the array grow, free when you are done.

aggaton
  • 3,066
  • 2
  • 25
  • 36
1

dynamic in C implies you will need to use one of [c][m]alloc to create memory for your strings. And 2D implies an array of char arrays. Assuming you know the number of strings and the longest string needed, the following will create memory to contain them:

char ** Create2DStr(ssize_t numStrings, ssize_t maxStrLen)
{
    int i;
    char **a = {0};
    a = calloc(numStrings, sizeof(char *));
    for(i=0;i<numStrings; i++)
    {
      a[i] = calloc(maxStrLen + 1, 1);
    }
    return a;
}

The following will free the memory created above:

void free2DStr(char ** a, ssize_t numStrings)
{
    int i;
    for(i=0;i<numStrings; i++)
    {
        if(a[i]) free(a[i]);
    }
    free(a);
}

These can be called like this:

...
char **strArray = {0};
strArray = Create2DStr(10, 20);
//Use strArray...
free2DStr(10);

Giving 10 arrays, each able to contain 20 char, plus a NULL. (The + 1 after maxStrLen provides the extra space for the NULL).

ryyker
  • 22,849
  • 3
  • 43
  • 87
  • Technically that's not 2D array or an array of arrays... it's a 1D array of pointers, each of which points to a separately allocated 1D array of `char`. An actual 2D array is also possible, but different. – Dmitri Jan 10 '17 at 20:21
  • What about `char (*arr)[21] = calloc(10, sizeof(*arr));`? – Dmitri Jan 10 '17 at 20:28
  • @Dmitri - Yes, that works well as long as the 21 is known at run-time. I have use something similar. But if not, then malloc or calloc are needed. Of coarse, there are _[variable length arrays](http://www.geeksforgeeks.org/variable-length-arrays-in-c-and-c/)_ from C99 and beyond. – ryyker Jan 10 '17 at 20:47
1

If you want to save each line of the file as a row in the array, use a 2D array of char:

char fileContents[NUM_LINES][LINE_LENGTH + 1]; // +1 for zero terminator

If you don't know how many lines you have up front, you'll need to do some memory management. First, you'll need to allocate an initial extent:

#define INITIAL_EXTENT 20 // or some good starting point

char (*fileContents)[LINE_LENGTH + 1] = malloc( sizeof *fileContents * INITIAL_EXTENT );
if ( !fileContents )
{
  // malloc failed; fatal error
  fprintf( stderr, "FATAL: could not allocate memory for array\n" );
  exit( EXIT_FAILURE );
}
size_t numRows = INITIAL_EXTENT; // number of rows in array
size_t rowsRead = 0;             // number of rows containing data

As you read from the file, you'll check to make sure you have room in the array; if you don't, you'll need to extend the array with a realloc call, which is a potentially expensive operation. A common technique is to double the size of the array each time you extend it - that minimizes the total number of realloc calls. The risk is some internal fragmentation if you double the array size because you need just one more row, but that's probably something you can analyze around:

char tmpBuf[LINE_LENGTH + 2]; // account for newline in input buffer

while ( fgets( tmpBuf, sizeof tmpBuf, inputFile ) )
{
  /**
   * Check to see if you have any room left in your array; if not, 
   * you'll need to extend it.  You'll probably want to factor this 
   * into its own function.
   */
  if ( rowsRead == numRows ) 
  {
    /**
     * Use a temporary variable for the result of realloc in case of failure
     */
    char (*tmp)[LINE_LENGTH + 1] = 
      realloc( fileContents, sizeof *fileContents * ( 2 * numRows ) );

    if ( !tmp )
    {
      /**
       * realloc failed - we couldn't extend the array any more.  
       * Break out of the loop. 
       */
      fprintf( stderr, "ERROR: could not extend fileContents array - breaking out of loop\n" );
      break;
    }
    /**
     * Otherwise, set fileContents to point to the new, extended buffer
     * and update the number of rows.  
     */
    fileContents = tmp;
    numRows *= 2;
  }

  // strip the newline from the input buffer
  char *newline = strchr( tmpBuf, '\n' );
  if ( newline )
    *newline = 0;

  strcpy( fileContents[rowsRead++], tmpBuf );
}
John Bode
  • 119,563
  • 19
  • 122
  • 198