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

R

Robert Oschler

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_label"] = 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.
 
K

Keith Thompson

Robert Oschler said:
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_label"] = 6;

I think that's the syntax Awk uses; Perl uses
$someArray{"a_key_label"} = 6;
(and refers to the data structure as a "hash", not as an "associative
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.
 
W

Walter Roberson

I am converting a Perl script over to "C" for a potential open source
project.
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.

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
 
K

Keith Thompson

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

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.
 
J

Joe Estock

Robert said:
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_label"] = 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.
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(struct config_settings *, char *, char *);
int config_set_ex(struct config_settings *, int, char *);
char *config_get(struct config_settings *, char *);
char *config_get_ex(struct config_settings *, int);
void rehash_config(struct config_settings *, char *);
int save_config(struct config_settings *, char *);
void deinit_config(struct config_settings *);
void trim(char *);
int init_config(struct config_settings *, char *);

#endif /* !defined(_CONFIG_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(struct 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 != NULL; i++)
{
if(!strcasecmp(config->key, key))
{
return(config_set_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(struct config_settings *config, int index, char *value)
{
char *tmp;

if((tmp = malloc(strlen(value) + 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(struct config_settings *config, char *key)
{
unsigned int i;

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

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

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(struct config_settings *config, char *filename)
{
deinit_config(config);
init_config(config, 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(struct 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,
"Automatically generated by geekbot via save_config");
for(i = 0; config->key != NULL; i++)
{
fprintf(fp, "%s = \"%s\"\n", config->key, config->value == NULL
? "" : config->value);
}

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(struct config_settings *config)
{
unsigned int i;

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

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

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++) ; /* nothing */
if(i > 0)
{
memmove(s, &s, strlen(&s) + 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(struct 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(char *) * 1);
config->value = malloc(sizeof(char *) * 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 = malloc(strlen(tokenptr) + 1)) == NULL)
return(false);
memset(config->key, '\0', strlen(tokenptr) + 1);
strcpy(config->key, tokenptr);

tokenptr = strtok(NULL, "=");

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

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

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

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

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

config->key = test;
config->key = NULL;

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

config->value = test;
config->value = NULL;
}
}

fclose(fp);

return(true);
}

--END config.c
 
J

Jonathan Bartlett

Robert said:
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:

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top