I am new to glib and its idioms. I used a GString to accumulate a buffer from the download of a URL. Now I want to print that buffer to stdout.
Right now I'm using code like
int len = catalogBuffer->len;
gchar *cat = g_string_free(catalogBuffer, FALSE);
catalogBuffer=0;
write(1, cat, len); // this might not do everything if the OS returns a partial write
g_free(cat);
but it just seems clumsy. Is there a better idiom to do this?
The string returned from g_string_free() is 0-terminated, so you can do this:
char *cat = g_string_free(catalogBuffer, FALSE);
g_print("%s\n", cat);
g_free(cat);
You can also read the nul-terminated string directly out of the GString:
const char *cat = catalogBuffer->str;
g_print ("%s\n", cat);
…
g_string_free (catalogBuffer, TRUE);
Related
I want to extract string that exist after first occurrence of "/"
For example a have a char *card in one of this form:
hw:0/Line
default/Master
and I need char *channel (Line, Master, ...)
In standard C can use "strcspn" and "strlen"
char *card, *channel;
int i;
i = strcspn(card, "/");
if (i == strlen(card)) {
channel = "Master";
} else {
card[i] = 0;
channel = card + i + 1;
}
How can I do this using glib string utility functions?
What's wrong with strcspn and strlen?
GLib reimplements a lot of stuff C99 and POSIX because it targets C89 compilers. It doesn't reimplement things like strlen or strcspn because there is no need to—they are in C89.
GLib also tends to implement UTF-8 versions of a lot of functions, and while there is a g_utf8_strlen I'm not aware of a g_utf8_strcspn I don't think it's really necessary in this case. If it is then you'll probably have to implement it yourself by iterating through the string with g_utf8_next_char and checking against the result of g_utf8_get_char or g_utf8_get_char_validated.
so what i am trying to do is whenever i touch a button, it calls a function with both a char and some variable number of arguments (in this case two ints that are both worth 24).
- (IBAction)buttonPressed:(id)sender {
number = 24;
printf_to_debug("some text %d %d ",number, number);
//printf(" %d %d ",number, number);
}
however, when i call the function it seems to always change my ints to some big number, which i don't understand.
void printf_to_debug (char* fmt, ...) {
va_list args;
va_start( args, fmt );
printf(fmt, args);
charString = [NSString stringWithUTF8String: fmt];
charString = [NSString stringWithFormat:charString, args];
debugTextString = [NSString stringWithFormat:#"%#\r%#",charString, debugTextString];
va_end( args );
}
does anyone see a problem with my code?
You have to use vprintf() to print the arguments given by a va_list:
va_list args;
va_start (args, fmt );
vprintf(fmt, args);
va_end( args );
The corresponding NSString method is initWithFormat:arguments::
NSString *s = [[NSString alloc] initWithFormat:#(fmt) arguments:args];
Let's take a look at what your code actually does.
The first important line is: printf_to_debug("some text %d %d ",number, number);
Which looks fine, it doesn't really matter what number is it'll be treated as an int anyways.
Next you initialize the variable arguments list which is fine but then you try and print using:
printf(fmt, args);
Now, When you use that code, you seem to want printf to treat each individual object in your variable arguments list as a separate parameter, however, printf doesn't care. All it sees is that you passed it a parameter. It doesn't know or care what the parameter was, all it knows and cares about is that you told it to treat the first parameter it was given as a decimal number (that's what the "%d" says). So now, it takes va_list and treats it as a number and prints it. Since you only provided one argument, the second number you get is complete garbage, and I suspect XCode tried to warn you about that (command line clang does).
Now for the simple solution:
When your trying to print a variable arguments list as seperate objects use the vprintf function. So in your case it would be vprintf(fmt, args);
I have a file format something like this
...
{string_length} {binary_string}
...
example:
...
10 abcdefghij
...
Is this possible to parse using lexer/yacc? There is no null terminator for the string, so I'm at a loss of how to tokenize that.
I'm currently using ply's lexer and yacc for this
You can't do it with a regular expression, but you can certainly extract the lexeme. You're not specific about how the length is terminated; here, I'm assuming that it is terminated by a single space character. I'm also assuming that yylval has some appropriate struct type:
[[:digit:]]+" " { unsigned long len = atol(yytext);
yylval.str = malloc(len);
yylval.len = len;
for (char *p = yylval.str; len; --len, ++p) {
int ch = input();
if (ch == EOF) { /* handle the lexical error */ }
*p = ch;
}
return BINARY_STRING;
}
There are other solutions (a start condition and a state variable for the count, for example), but I think the above is the simplest.
Pretty sure the answer is No on this one, but it's painful enough I have to ask: Is there a CFNumber equivalent to CFString's CFSTR macro? To avoid this sort of thing:
char one = 1;
CFNumberRef cfONE = CFNumberCreate(kCFAllocatorDefault, kCFNumberCharType, &one);
if (cfONE != NULL) {
... finally I can compare something to the number 1! ...
CFRelease(cfONE);
} else {
// not likely, but possible, if you really want to be conservative
}
Note that I'm not using Objective-C in this particular code.
Eric
If you plan on using this function multiple times, you could the static modifier and stop worrying about deallocation:
static CFNumberRef cfONE = NULL;
if (cfONE == NULL) {
static char one = 1;
cfONE = CFNumberCreate(kCFAllocatorDefault, kCFNumberCharType, &one);
assert (cfONE != NULL); // Oh no, destroy the world!
}
// ... finally I can compare something to the number 1! ...
So long as you have static, the static analyzer will leave you alone on the leak issue since it is an expected constant size memory allocation, O(1) rather than O(n) where n is the number of executions.
There are several ways to make this a macro. I came up with this lazy one:
#define CFNUMDEF(name, type, numberType, value) \
static CFNumberRef name = NULL; \
if ( name == NULL) { \
static type val = value ;\
name = CFNumberCreate(kCFAllocatorDefault, numberType , &val);\
assert ( name != NULL); \
}
CFNUMDEF(cfONE, char, kCFNumberCharType, 1);
// ... finally I can compare something to the number 1! ...
CFSTR is a little different from your case
CFSTR() allows creation of compile-time constant CFStringRefs; the argument
should be a constant C-string.
CFSTR(), not being a "Copy" or "Create" function, does not return a new
reference for you. So, you should not release the return value. This is
much like constant C or Pascal strings --- when you use "hello world"
in a program, you do not free it.
Where as the object you create with CFNumberCreate will be owned by the caller so you may still want to keep the word create in the name to make this clear.
You could always make a helper function just to avoid passing the same arguments over and over
CFNumberRef PSNumberCreateWithChar(x)
{
return CFNumberCreate(kCFAllocatorDefault, kCFNumberCharType, &x);
}
CFNumberRef and NSNumber are toll free bridged so you can use the NSNumber literal syntax and cast the result.
I have a binary file I've loaded using an NSData object. Is there a way to locate a sequence of characters, 'abcd' for example, within that binary data and return the offset without converting the entire file to a string? Seems like it should be a simple answer, but I'm not sure how to do it. Any ideas?
I'm doing this on iOS 3 so I don't have -rangeOfData:options:range: available.
I'm going to award this one to Sixteen Otto for suggesting strstr. I went and found the source code for the C function strstr and rewrote it to work on a fixed length Byte array--which incidentally is different from a char array as it is not null terminated. Here is the code I ended up with:
- (Byte*)offsetOfBytes:(Byte*)bytes inBuffer:(const Byte*)buffer ofLength:(int)len;
{
Byte *cp = bytes;
Byte *s1, *s2;
if ( !*buffer )
return bytes;
int i = 0;
for (i=0; i < len; ++i)
{
s1 = cp;
s2 = (Byte*)buffer;
while ( *s1 && *s2 && !(*s1-*s2) )
s1++, s2++;
if (!*s2)
return cp;
cp++;
}
return NULL;
}
This returns a pointer to the first occurrence of bytes, the thing I'm looking for, in buffer, the byte array that should contain bytes.
I call it like this:
// data is the NSData object
const Byte *bytes = [data bytes];
Byte* index = [self offsetOfBytes:tag inBuffer:bytes ofLength:[data length]];
Convert your substring to an NSData object, and search for those bytes in the larger NSData using rangeOfData:options:range:. Make sure that the string encodings match!
On iPhone, where that isn't available, you may have to do this yourself. The C function strstr() will give you a pointer to the first occurrence of a pattern within the buffer (as long as neither contain nulls!), but not the index. Here's a function that should do the job (but no promises, since I haven't tried actually running it...):
- (NSUInteger)indexOfData:(NSData*)needle inData:(NSData*)haystack
{
const void* needleBytes = [needle bytes];
const void* haystackBytes = [haystack bytes];
// walk the length of the buffer, looking for a byte that matches the start
// of the pattern; we can skip (|needle|-1) bytes at the end, since we can't
// have a match that's shorter than needle itself
for (NSUInteger i=0; i < [haystack length]-[needle length]+1; i++)
{
// walk needle's bytes while they still match the bytes of haystack
// starting at i; if we walk off the end of needle, we found a match
NSUInteger j=0;
while (j < [needle length] && needleBytes[j] == haystackBytes[i+j])
{
j++;
}
if (j == [needle length])
{
return i;
}
}
return NSNotFound;
}
This runs in something like O(nm), where n is the buffer length, and m is the size of the substring. It's written to work with NSData for two reasons: 1) that's what you seem to have in hand, and 2) those objects already encapsulate both the actual bytes, and the length of the buffer.
If you're using Snow Leopard, a convenient way is the new -rangeOfData:options:range: method in NSData that returns the range of the first occurrence of a piece of data. Otherwise, you can access the NSData's contents yourself using its -bytes method to perform your own search.
I had the same problem.
I solved it doing the other way round, compared to the suggestions.
first, I reformat the data (assume your NSData is stored in var rawFile) with:
NSString *ascii = [[NSString alloc] initWithData:rawFile encoding:NSAsciiStringEncoding];
Now, you can easily do string searches like 'abcd' or whatever you want using the NSScanner class and passing the ascii string to the scanner. Maybe this is not really efficient, but it works until the -rangeOfData method will be available for iPhone also.