Connecting Tech Pros Worldwide Forums | Help | Site Map

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

Robert Oschler
Guest
 
Posts: n/a
#1: Nov 14 '05
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.



Keith Thompson
Guest
 
Posts: n/a
#2: Nov 14 '05

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


"Robert Oschler" <no-mail-please@nospam.com> 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_label"] = 6;[/color]

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.

--
Keith Thompson (The_Other_Keith) 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.
Walter Roberson
Guest
 
Posts: n/a
#3: Nov 14 '05

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


In article <BIGdndiNHP9t78XfRVn-1A@adelphia.com>,
Robert Oschler <no-mail-please@nospam.com> 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.
Keith Thompson
Guest
 
Posts: n/a
#4: Nov 14 '05

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


roberson@ibd.nrc-cnrc.gc.ca (Walter Roberson) writes:[color=blue]
> In article <BIGdndiNHP9t78XfRVn-1A@adelphia.com>,
> Robert Oschler <no-mail-please@nospam.com> 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_Keith) 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.
Joe Estock
Guest
 
Posts: n/a
#5: Nov 14 '05

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_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.
>
>[/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(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[i] != NULL; i++)
{
if(!strcasecmp(config->key[i], 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[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(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[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(struct 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(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[i] = malloc(strlen(tokenptr) + 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(tokenptr, &tokenptr[1], strlen(&tokenptr[1]) + 1);

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

if(strlen(tokenptr) == 0)
{
config->value[i] = NULL;
}
else
{
if((config->value[i] = malloc(strlen(tokenptr) + 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
Jonathan Bartlett
Guest
 
Posts: n/a
#6: Nov 14 '05

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
http://www.cafeshops.com/bartlettpublish.8640017
Closed Thread