C data container similar to Perl style key-value array?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Robert Oschler

    C data container similar to Perl style key-value array?

    I am converting a Perl script over to "C" for a potential open source
    project. I need some open source "C" code that will give me the same
    functionality of a Perl Style associative array:

    someArray["a_key_labe l"] = 6;

    I know I can't get the same syntactic sugar as Perl offers, with the usage
    of a string as the array key surrounded by square brackets. I just want the
    general functionality, that's all. That is, a data container that will
    maintain an internal list of key value pairs, with the ability to walk the
    keys in sorted order. Also, the container should "auto-insert" a key-value
    pair if it doesn't already exist in the container, when a particular key
    value pair is referenced.

    Do any of you know of any open source "C" code that provides a container
    with the same behavior as Perl style arrays? URL's if you got 'em please.

    Thanks.


  • Keith Thompson

    #2
    Re: C data container similar to Perl style key-value array?

    "Robert Oschler" <no-mail-please@nospam.c om> writes:[color=blue]
    > I am converting a Perl script over to "C" for a potential open source
    > project. I need some open source "C" code that will give me the same
    > functionality of a Perl Style associative array:
    >
    > someArray["a_key_labe l"] = 6;[/color]

    I think that's the syntax Awk uses; Perl uses
    $someArray{"a_k ey_label"} = 6;
    (and refers to the data structure as a "hash", not as an "associativ e
    array").

    In a Perl hash, the key is a string, and the value can be any
    arbitrary scalar value (string, number, reference, undef). You're not
    going to get the same functionality in C without substantial syntactic
    overhead. The closest C equivalent to a Perl scalar would be a
    structure consisting of an enumeration value indicating a type, and a
    union of all the types that can be stored in it. The job is simpler
    if you can limit the stored values to one type (int, char*, etc.).

    Perl, unlike C, also takes care of storage management for you. For
    example, if you do:
    $foo{"bar"} = "some string value";
    $foo{"bar"} = "Some Other String Value";
    the second assignment will cause the memory allocated for "some string
    value" to be deallocated. In C, you'll have to take care of this
    yourself (though a well-designed interface can make it easier).

    <OT>
    I think some of container classes in the C++ standard library do some
    of what you're looking for. You might consider writing a C wrapper
    for one of them -- or just implementing the project in C++. If you go
    that route, of course, this isn't the place to ask about it.
    </OT>

    Sorry I don't have a good answer for you, but perhaps I've helped nail
    down the question a bit.

    --
    Keith Thompson (The_Other_Keit h) kst-u@mib.org <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    We must do something. This is something. Therefore, we must do this.

    Comment

    • Walter Roberson

      #3
      Re: C data container similar to Perl style key-value array?

      In article <BIGdndiNHP9t78 XfRVn-1A@adelphia.com >,
      Robert Oschler <no-mail-please@nospam.c om> wrote:[color=blue]
      >I am converting a Perl script over to "C" for a potential open source
      >project.[/color]
      [color=blue]
      >I know I can't get the same syntactic sugar as Perl offers, with the usage
      >of a string as the array key surrounded by square brackets. I just want the
      >general functionality, that's all. That is, a data container that will
      >maintain an internal list of key value pairs, with the ability to walk the
      >keys in sorted order.[/color]

      There is no built-in ability in perl to walk the keys in sorted
      order. If you walk the keys, you will get them in hash order,
      and the perl implementers guarantee that the order *will* change
      from time to time with different versions of perl. If I recall
      correctly, newer versions of perl will use a -different- order
      each time unless you use some perl magic.

      In order to walk the elements in key-sorted order in perl, you have
      to extract the keys, sort the resulting list according to
      your sort criteria, and then iterate through the resulting
      list
      --
      Usenet is like a slice of lemon, wrapped around a large gold brick.

      Comment

      • Keith Thompson

        #4
        Re: C data container similar to Perl style key-value array?

        roberson@ibd.nr c-cnrc.gc.ca (Walter Roberson) writes:[color=blue]
        > In article <BIGdndiNHP9t78 XfRVn-1A@adelphia.com >,
        > Robert Oschler <no-mail-please@nospam.c om> wrote:[color=green]
        >>I am converting a Perl script over to "C" for a potential open source
        >>project.[/color]
        >[color=green]
        >>I know I can't get the same syntactic sugar as Perl offers, with the usage
        >>of a string as the array key surrounded by square brackets. I just want the
        >>general functionality, that's all. That is, a data container that will
        >>maintain an internal list of key value pairs, with the ability to walk the
        >>keys in sorted order.[/color]
        >
        > There is no built-in ability in perl to walk the keys in sorted
        > order. If you walk the keys, you will get them in hash order,
        > and the perl implementers guarantee that the order *will* change
        > from time to time with different versions of perl. If I recall
        > correctly, newer versions of perl will use a -different- order
        > each time unless you use some perl magic.
        >
        > In order to walk the elements in key-sorted order in perl, you have
        > to extract the keys, sort the resulting list according to
        > your sort criteria, and then iterate through the resulting
        > list[/color]

        Which, as it turns out, is very easy to do, since "sort" is a built-in
        operator:

        foreach my $key (sort keys %hash) {
        print "$key => $hash{$key}\n";
        }

        Or, if that's too verbose for you:

        map { print "$_ => $hash{$_}\n" } sort keys %hash;

        A C version of this would almost certainly be more verbose, with more
        explicit code to take care of things that Perl does behind the scenes.

        --
        Keith Thompson (The_Other_Keit h) kst-u@mib.org <http://www.ghoti.net/~kst>
        San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
        We must do something. This is something. Therefore, we must do this.

        Comment

        • Joe Estock

          #5
          Re: C data container similar to Perl style key-value array?

          Robert Oschler wrote:[color=blue]
          > I am converting a Perl script over to "C" for a potential open source
          > project. I need some open source "C" code that will give me the same
          > functionality of a Perl Style associative array:
          >
          > someArray["a_key_labe l"] = 6;
          >
          > I know I can't get the same syntactic sugar as Perl offers, with the usage
          > of a string as the array key surrounded by square brackets. I just want the
          > general functionality, that's all. That is, a data container that will
          > maintain an internal list of key value pairs, with the ability to walk the
          > keys in sorted order. Also, the container should "auto-insert" a key-value
          > pair if it doesn't already exist in the container, when a particular key
          > value pair is referenced.
          >
          > Do any of you know of any open source "C" code that provides a container
          > with the same behavior as Perl style arrays? URL's if you got 'em please.
          >
          > Thanks.
          >
          >[/color]
          You will have to do the sorting yourself, however I have attached my
          implementation which I use in other projects that works great for this
          type of thing (it's actually a configuration parser). Hope this helps.

          Joe Estock

          --BEGIN config.h:

          /* $Id: config.h,v 1.3 2005/03/06 00:23:49 joe Exp $ */

          #ifndef _CONFIG_H
          #define _CONFIG_H

          struct config_settings
          {
          char **key;
          char **value;
          };

          int config_set(stru ct config_settings *, char *, char *);
          int config_set_ex(s truct config_settings *, int, char *);
          char *config_get(str uct config_settings *, char *);
          char *config_get_ex( struct config_settings *, int);
          void rehash_config(s truct config_settings *, char *);
          int save_config(str uct config_settings *, char *);
          void deinit_config(s truct config_settings *);
          void trim(char *);
          int init_config(str uct config_settings *, char *);

          #endif /* !defined(_CONFI G_H) */

          --END config.h

          --BEGIN config.c

          /* $Id: config.c,v 1.6 2005/03/12 07:59:40 joe Exp $ */

          /**
          * This is a general configuration parser. Nothing fancy
          * is done here and nothing specific to the main configuration
          * file is handled here. This file contains the building blocks
          * for handling the configuration files by parsing the file and
          * splitting up the various configuration settings and their
          * values.
          */

          #include <string.h>
          #include <stdlib.h>
          #include <unistd.h>
          #include <stdio.h>
          #include <sys/types.h>
          #include <sys/stat.h>
          #include <fcntl.h>
          #include "config.h"
          #include "stdbool.h"

          /**
          * Sets the specified key with the specified value. If an
          * error is encountered, -1 is returned otherwise the index
          * of the newly inserted key is returned.
          *
          * @param config Pointer to a previously malloced config_settings
          * @param key Key to change or set
          * @param value value for key
          * @return -1 on failure, index of item otherwise
          */
          int config_set(stru ct config_settings *config, char *key, char *value)
          {
          unsigned int i;

          if(key == NULL || config == NULL || config->key == NULL)
          {
          return(-1);
          }

          for(i = 0; config->key[i] != NULL; i++)
          {
          if(!strcasecmp( config->key[i], key))
          {
          return(config_s et_ex(config, i, value));
          }
          }

          return(-1);
          }

          /**
          * Sets the specified key with the specified value. If an
          * error is encountered, -1 is returned otherwise the index
          * of the newly inserted key is returned.
          *
          * @see config_set()
          * @param config Pointer to a previously malloced config_settings
          * @param index index of key to change or set
          * @param value value for key
          * @return -1 on failure, index of item otherwise
          */
          int config_set_ex(s truct config_settings *config, int index, char *value)
          {
          char *tmp;

          if((tmp = malloc(strlen(v alue) + 1)) == NULL)
          {
          perror("malloc" );
          return(-1);
          }
          memset(tmp, '\0', strlen(value) + 1);
          strcpy(tmp, value);

          free(config->value[index]);
          config->value[index] = tmp;

          return(index);
          }

          /**
          * Returns the value associated with key
          *
          * @param config Pointer to a previously malloced config_settings
          * @param key Key to retreive value for
          * @return Value of key or NULL if nonexistant or empty
          */
          char *config_get(str uct config_settings *config, char *key)
          {
          unsigned int i;

          if(key == NULL || config == NULL || config->key == NULL)
          {
          return(NULL);
          }

          for(i = 0; config->key[i] != NULL; i++)
          {
          if(!strcasecmp( config->key[i], key))
          {
          return(config->value[i]);
          }
          }

          return(NULL);
          }

          /**
          * Returns the value associated with key
          *
          * @see config_get()
          * @param config Pointer to a previously malloced config_settings
          * @param index Index of key to retreive value for
          * @return Value of key or NULL if nonexistant or empty
          */
          char *config_get_ex( struct config_settings *config, int index)
          {
          return(config->value[index]);
          }

          /**
          * Reloads the configuration file
          *
          * @see save_config()
          * @param config Pointer to a previously malloced config_settings
          * @param filename Filename to read configuration data from
          */
          void rehash_config(s truct config_settings *config, char *filename)
          {
          deinit_config(c onfig);
          init_config(con fig, filename);
          }

          /**
          * Saves the new configuration parameters to specified file
          *
          * @see rehash_config()
          * @param config Pointer to a previously malloced config_settings
          * @param filename Filename to store keys and values in
          * @return true on success, false on failure
          */
          bool save_config(str uct config_settings *config, char *filename)
          {
          FILE *fp;
          unsigned int i;

          if(config == NULL || config->key == NULL || filename == NULL)
          {
          return(false);
          }

          if((fp = fopen(filename, "w")) == NULL)
          {
          return(false);
          }

          fprintf(fp, "# %s - %s\n",
          filename,
          "Automatica lly generated by geekbot via save_config");
          for(i = 0; config->key[i] != NULL; i++)
          {
          fprintf(fp, "%s = \"%s\"\n", config->key[i], config->value[i] == NULL
          ? "" : config->value[i]);
          }

          fclose(fp);
          return(true);
          }

          /**
          * Free up any resources used for specified configuration
          *
          * @see init_config()
          * @param config Pointer to a previously malloced config_settings
          */
          void deinit_config(s truct config_settings *config)
          {
          unsigned int i;

          if(config == NULL || (config->key == NULL && config->value == NULL))
          {
          return;
          }

          for(i = 0; config->key[i] != NULL; i++)
          {
          if(config->key[i] != NULL) { free(config->key[i]); }
          if(config->value[i] != NULL) { free(config->value[i]); }
          }

          free(config->key);
          free(config->value);
          }

          /**
          * Removes leading and trailing spaces as well as any
          * newlines from the end of a string
          *
          * @param s string to trim
          */
          void trim(char *s)
          {
          unsigned int len = strlen(s);
          unsigned int i = 0;

          if(len == 0)
          {
          return;
          }

          for(i = 0; i < len && isspace(s[i]); i++) ; /* nothing */
          if(i > 0)
          {
          memmove(s, &s[i], strlen(&s[i]) + 1);
          }

          if((len = strlen(s)) == 0)
          {
          return;
          }

          i = len;
          while(s[i - 1] == '\r' || s[i - 1] == '\n' || isspace(s[i - 1]))
          {
          s[i - 1] = '\0';
          i--;
          }

          /*while(s[strlen(s) - 1] == '\r' || s[strlen(s) - 1] == '\n' ||
          isspace(s[strlen(s) - 1]))
          {
          s[strlen(s) - 1] = '\0';
          }*/
          }

          /**
          * Initializes the configuration found in specified filename
          *
          * @param config Pointer to a previously malloced config_settings
          * @param filename Filename to read keys and values from
          * @return true on success, false on failure
          */
          int init_config(str uct config_settings *config, char *filename)
          {
          FILE *fp;
          char buf[2048];
          char *tokenptr;
          unsigned int i;
          char **test;

          if(config == NULL)
          return(false);

          if(!(fp = fopen(filename, "r")))
          {
          return(false);
          }

          config->key = malloc(sizeof(c har *) * 1);
          config->value = malloc(sizeof(c har *) * 1);
          config->key[0] = NULL;
          config->value[0] = NULL;

          memset(buf, '\0', 2048);

          i = 0;
          while(!feof(fp) )
          {
          memset(buf, '\0', 2048);
          if(fgets(buf, 2048, fp) == NULL)
          break;

          trim(buf);

          if(buf == NULL || strlen(buf) == 0 || buf[0] == '#')
          continue;

          tokenptr = strtok(buf, "=");

          if(tokenptr != NULL)
          {
          trim(tokenptr);
          if((config->key[i] = malloc(strlen(t okenptr) + 1)) == NULL)
          return(false);
          memset(config->key[i], '\0', strlen(tokenptr ) + 1);
          strcpy(config->key[i], tokenptr);

          tokenptr = strtok(NULL, "=");

          if(tokenptr != NULL)
          {
          trim(tokenptr);

          if(tokenptr[0] == '"')
          memmove(tokenpt r, &tokenptr[1], strlen(&tokenpt r[1]) + 1);

          if(tokenptr[strlen(tokenptr ) - 1] == '"')
          tokenptr[strlen(tokenptr ) - 1] = '\0';

          if(strlen(token ptr) == 0)
          {
          config->value[i] = NULL;
          }
          else
          {
          if((config->value[i] = malloc(strlen(t okenptr) + 1)) == NULL)
          return(false);
          memset(config->value[i], '\0', strlen(tokenptr ) + 1);
          strcpy(config->value[i], tokenptr);
          }
          }
          else
          {
          config->value[i] = malloc(2);
          strcpy(config->value[i], "\0");
          }

          i++;
          if((test = realloc(config->key, sizeof(char *) * (i + 1))) == NULL)
          return(false);

          config->key = test;
          config->key[i] = NULL;

          if((test = realloc(config->value, sizeof(char *) * (i + 1))) == NULL)
          return(false);

          config->value = test;
          config->value[i] = NULL;
          }
          }

          fclose(fp);

          return(true);
          }

          --END config.c

          Comment

          • Jonathan Bartlett

            #6
            Re: C data container similar to Perl style key-value array?

            Robert Oschler wrote:[color=blue]
            > I am converting a Perl script over to "C" for a potential open source
            > project. I need some open source "C" code that will give me the same
            > functionality of a Perl Style associative array:[/color]

            As others mentioned, you have to do the sorting yourself, but you might
            look at the glib library (part of gtk+, but not dependent on having X
            running). It has a number of utility functions for C, that makes C a
            lot easier to use.

            Jon
            ----
            Learn to program using Linux assembly language

            Comment

            Working...