get last integer value from char array - c

Consider a char array like this:
43 234 32 32
I want the last value that is 32 in integer form.
The string size/length is not known. In the above example there are 4 numbers, but the size will vary.
How can this be done?

i have copied these value from the file onto char array.now i want the last number in integer variable
When you were copying,add a counter of # of characters copied. Then do this
int count = 0;
char c;
while(c = readCharFromFile()) {
array[count++] = c;
}
int last = array[count - 1];

There are many ways to solve this.
Convert every token (space delimited string) into a number and when the tokens run out return the last value converted.
Scan the line for tokens until you get to the end and then convert the last token into a number.
Start at the end of the line. Skip spaces and store digits until the first space is encountered and then convert the result to a number.
Split the string into an array of strings and convert the last one into a number.
I could go on and on but you get the idea I hope.

int getLastInt(char *data)
{
size_t i = strlen(data);
if(!i--) return -1; // failure
for(;i;--i)
{
if(data[i] == ' ')
{
return atoi(&data[i+1]);
}
}
return -1; // failure
}
Should work as long as the data has a space + actual text.
You could also skip the strlen and just loop forward, which could be faster depending on your system's strlen.

If there is no trailing whitespace on the line:
int last_int(const char *s)
{
const char *ptr = strrchr(s, ' ');
if (ptr == NULL) {
ptr = s;
} else {
ptr++;
}
return atoi(ptr);
}
If there can be trailing whitespace, then you'll need to do something like what ProdigySim suggested, but with more states, to walk backwards past the trailing whitespace (if any), then past the number, then call atoi(). No matter what you do, you'll need to watch out for boundary conditions and edge cases and decide how you want to handle them.

I guess you want to use a combination of strtok_r and atoi

Related

Removing a target character from a string using sscanf

I've recently been learning about different conversion specifiers, but I am struggling to use one of the more complex conversion specifiers. The one in question being the bracket specifier (%[set]).
To my understanding, from what I've read, using %[set] where any string matching the sequence of characters in set (the scanset) is consumed and assigned, and using %[^set] has the opposite effect; in essence consuming and assigning any string that does not contain the sequence of characters in the scanset.
That's my understanding, albeit roughly explained. I was trying to use this specifier with sscanf to remove a specified character from a string using sscanf:
sscanf(str_1, "%[^#]", str_2);
Suppose that str_1 contains "OH#989". My intention is to store this string in str_2, but removing the hash character in the process. However, sscanf stops reading at the hash character, storing only "OH" when I am intending to store "OH989".
Am I using the correct method in the wrong way, or am I using the wrong method altogether? How can I correctly remove/extract a specified character from a string using sscanf? I know this is possible to achieve with other functions and operators, but ideally I am hoping to use sscanf.
The scanset matches a sequence of (one or more) characters that either do or don't match the contents of the scanset brackets. It stops when it comes across the first character that isn't in the scanset. To get the two parts of your string, you'd need to use something like:
sscanf(str_1, "%[^#]#%[^#]", str_2, str_3);
We can negotiate on the second conversion specification; it might be that %s is sufficient, or some other scanset is appropriate. But this would give you the 'before #' and 'after #' strings that could then be concatenated to give the desired result string.
I guess, if you really want to use sscanf for the purpose of removing a single target character, you could do this:
char str_2[strlen(str_1) + 1];
if (sscanf(str_1, "%[^#]", str_2) == 1) {
size_t len = strlen(str_2);
/* must verify if a '#' was found at all */
if (str_1[len] != '\0') {
strcpy(str_2 + len, str_1 + len + 1);
}
} else {
/* '#' is the first character */
strcpy(str_2, str_1 + 1);
}
As you can see, sscanf is not the right tool for the job, because it has many quirks and shortcomings. A simple loop is more efficient and less error prone. You could also parse str_1 into 2 separate strings with sscanf(str_1, "%[^#]#%[\001-\377]", str_2, str_3); and deal with the 3 possible return values:
char str_2[strlen(str_1) + 1];
char str_3[strlen(str_1) + 1];
switch (sscanf(str_1, "%[^#]#%[\001-\377]", str_2, str_3)) {
case 0: /* empty string or initial '#' */
strcpy(str_2, str_1 + (str_1[0] == '#'));
break;
case 1: /* no '#' or no trailing part */
break;
case 2: /* general case */
strcat(str_2, str_3);
break;
}
/* str_2 hold the result */
Removing a target character from a string using sscanf
sscanf() is not the best tool for this task, see far below.
// Not elegant code
// Width limits omitted for brevity.
str_2[0] = '\0';
char *p = str_2;
// Test for the end of the string
while (*str_1) {
int n; // record stopping offset
int cnt = sscanf(str_1, "%[^#]%n", p, &n);
if (cnt == 0) { // first character is a #
str_1++; // advance to next
} else {
str_1 += n; // advance n characters
p += n;
}
}
Simple loop:
Remove the needles from a haystack and save the hay in a bail.
char needle = '#';
assert(needle);
do {
while (*haystack == needle) haystack++;
} while (*bail++ = *haystack++);
With the 2nd method, code could use haystack = bail = str_1

Find the length of an integer within a string

If I've got a text file like:
8f5
I can easily use strstrto parse the values 8 and 5 out of it.
As such:
//while fgets.. etc (other variables and declarations before it)
char * ptr = strstr(str,"f");
if(ptr != NULL)
{
int a = atol(ptr-1); // value of 8
int b = atol(ptr+1); // value of 5
}
But what if the values where two decimals long? I could add +2 and -2 to each atol call. But I can't predict when the values are less than 10 or greater, for instance
12f6
or 15f15 As the values are random each time (i.e either one decimal or two). Is there a way to check the length of the values between the string, and then use atol()?
Use atol(str) and atol(ptr+1), if I am reading the question correctly. This will get you the two numbers separated by the f, regardless of how long they are.
Set *ptr = '\0' first if you don't wish to rely on the fact that garbage characters stop atol from parsing.
If the text is always similar to the one you posted, then you can get the three parts of the string with the following code, and you can parse another token if there is a white space between them
#include <ctype.h>
#include <stdio.h>
int main(void)
{
char string[] = "12f5 1234x2912";
char *next;
next = string;
while (*next != '\0') /* While not at the end of the string */
{
char separator[100];
size_t counter;
int firstNumber;
int secondNumber;
/* Get the first number */
firstNumber = strtol(next, &next, 10);
counter = 0;
/* Skip all non-numeric characters and store them in `separator' */
while ((*next != '\0') && (isdigit(*next) == 0))
separator[counter++] = *next++;
/* nul terminate `separator' */
separator[counter] = '\0';
/* extract the second number */
secondNumber = strtol(next, &next, 10);
/* show me how you did it */
printf("%d:%s:%d\n", firstNumber, separator, secondNumber);
/* skip any number of white space characters */
while ((*next != '\0') && (isspace(*next) != 0))
next++;
}
}
in the example above you can see that there are to strings being parsed, you can read the strtol() manual page to understand why this algorithm works.
Normally you should not use atoi() or atol() functions because you cant validate the input string, since there is no way to know whether the function succeded or not.

character array is not getting printed using %s

1.While I am trying to display conv it is not showing anything, but when i print one one element using subscript i am able to see the contents.
2. Program to convert decimal to binary, octal, hexadecimal
#include<stdio.h>
int main()
{
convbase(23,2);
return 0;
}
int convbase(int num, int base)
{
char conv[33] = {' '};
char *ptr = &conv[32];
conv[32] = '\0';
do
{
*--ptr = "0123456789ABCDEF"[num % base];
num = num/base;
}while(num);
if(base == 16)
{
*--ptr = 'x';
*--ptr = '0';
}
if(base == 8)
{
*--ptr = '0';
}
printf("Decimal to base %d is :\t%s\n",base,conv);
return 0;
}
change
printf("Decimal to base %d is :\t%s\n",base,conv);
to
printf("Decimal to base %d is :\t%s\n",base,ptr);
or
move the contents (ptr - to end of string) to start of conv array
another alternative is to instead of writing from end of conv to write from start and then when you are done call strrev() to reverse it.
printf("Decimal to base %d is :\t%s\n",base,strrev(conv));
You are filling your array from the end towards the front. In your example, the bit string is in the array elements 27...31. Thus, the 1st element contains \0, and the char array will be considered as an empty string, and nothing is printed.
You can check this easily by setting a breakpoint at the print statement.
You most propably assume that the following line initialises conv to all blanks:
char conv[33] = {' '};
This is not the case. With the above code conv has its first byte set to blank and all other bytes set to 0. So the result would only be printed if it had at least 31 digits.
You might consider using the follwong code to intialise conv:
char conv[33] = {0}; /* default init: set conv to all zeros */
memset(conv, ' ', sizeof(conv) - 1); /* re-set all but the last to blank */
This line:
conv[32] = '\0';
is not needed anymore than.
I do agree with Reinhard Männer that using a debugger would have helped you a lot to finding this out by yourself.
I think you're doing one -- too much.
While building the string, ptr always points to where you're going to write the next character (if there is a next character). This place is always initialized with null bytes (except for conv[0], which is a space.
So when calling printf, ptr points to null bytes, which is an empty string.
ptr++ before printf should solve it.
I do agree with #alk.
You were initializing only the first element of the array with blank and other elements were initialized to 0 because of which you were getting blank line.
Actually line is not blank, printf prints only first character which is blank ' ' and when it encounter 0 it stops.
You can verify it by replacing ' ' with any other character. ofcourse except '\0'

sscanf doesn't move, scanning same integer everytime

I have a string that has ints and I'm trying to get all the ints into another array. When sscanf fails to find an int I want the loop to stop. So, I did the following:
int i;
int getout = 0;
for (i = 0; i < bsize && !getout; i++) {
if (!sscanf(startbuffer, "%d", &startarray[i])) {
getout = 1;
}
}
//startbuffer is a string, startarray is an int array.
This results in having all the elements of startarray to be the first char in startbuffer.
sscanf works fine but it doesn't move onto the next int it just stays at the first position.
Any idea what's wrong? Thanks.
The same string pointer is passed each time you call sscanf. If it were to "move" the input, it would have to move all the bytes of the string each time which would be slow for long strings. Furthermore, it would be moving the bytes that weren't scanned.
Instead, you need to implement this yourself by querying it for the number of bytes consumed and the number of values read. Use that information to adjust the pointers yourself.
int nums_now, bytes_now;
int bytes_consumed = 0, nums_read = 0;
while ( ( nums_now =
sscanf( string + bytes_consumed, "%d%n", arr + nums_read, & bytes_now )
) > 0 ) {
bytes_consumed += bytes_now;
nums_read += nums_now;
}
Convert the string to a stream, then you can use fscanf to get the integers.
Try this.
http://www.gnu.org/software/libc/manual/html_node/String-Streams.html
You are correct: sscanf indeed does not "move", because there is nothing to move. If you need to scan a bunch of ints, you can use strtol - it tells you how much it read, so you can feed the next pointer back to the function on the next iteration.
char str[] = "10 21 32 43 54";
char *p = str;
int i;
for (i = 0 ; i != 5 ; i++) {
int n = strtol(p, &p, 10);
printf("%d\n", n);
}
This is the correct behavior of sscanf. sscanf operates on a const char*, not an input stream from a file, so it will not store any information about what it has consumed.
As for the solution, you can use %n in the format string to obtain the number of characters that it has consumed so far (this is defined in C89 standard).
e.g. sscanf("This is a string", "%10s%10s%n", tok1, tok2, &numChar); numChar will contain the number of characters consumed so far. You can use this as an offset to continue scanning the string.
If the string only contains integers that doesn't exceed the maximum value of long type (or long long type), use strtol or strtoll. Beware that long type can be 32-bit or 64-bit, depending on the system.

Determining the length of an array for memory efficiency

Write a function in C language that:
Takes as its only parameter a sentence stored in a string (e.g., "This is a short sentence.").
Returns a string consisting of the number of characters in each word (including punctuation), with spaces separating the numbers. (e.g., "4 2 1 5 9").
I wrote the following program:
int main()
{
char* output;
char *input = "My name is Pranay Godha";
output = numChar(input);
printf("output : %s",output);
getch();
return 0;
}
char* numChar(char* str)
{
int len = strlen(str);
char* output = (char*)malloc(sizeof(char)*len);
char* out = output;
int count = 0;
while(*str != '\0')
{
if(*str != ' ' )
{
count++;
}
else
{
*output = count+'0';
output++;
*output = ' ';
output++;
count = 0;
}
str++;
}
*output = count+'0';
output++;
*output = '\0';
return out;
}
I was just wondering that I am allocating len amount of memory for output string which I feel is more than I should have allocated hence there is some wasting of memory. Can you please tell me what can I do to make it more memory efficient?
I see lots of little bugs. If I were your instructor, I'd grade your solution at "C-". Here's some hints on how to turn it into "A+".
char* output = (char*)malloc(sizeof(char)*len);
Two main issues with the above line. For starters, you are forgetting to "free" the memory you allocate. But that's easily forgiven.
Actual real bug. If your string was only 1 character long (e.g. "x"), you would only allocate one byte. But you would likely need to copy two bytes into the string buffer. a '1' followed by a null terminating '\0'. The last byte gets copied into invalid memory. :(
Another bug:
*output = count+'0';
What happens when "count" is larger than 9? If "count" was 10, then *output gets assigned a colon, not "10".
Start by writing a function that just counts the number of words in a string. Assign the result of this function to a variable call num_of_words.
Since you could very well have words longer than 9 characters, so some words will have two or more digits for output. And you need to account for the "space" between each number. And don't forget the trailing "null" byte.
If you think about the case in which a 1-byte unsigned integer can have at most 3 chars in a string representation ('0'..'255') not including the null char or negative numbers, then sizeof(int)*3 is a reasonable estimate of the maximum string length for an integer representation (not including a null char). As such, the amount of memory you need to alloc is:
num_of_words = countWords(str);
num_of_spaces = (num_of_words > 0) ? (num_of_words - 1) : 0;
output = malloc(num_of_spaces + sizeof(int)*3*num_of_words + 1); // +1 for null char
So that's a pretty decent memory allocation estimate, but it will definitely allocate enough memory in all scenarios.
I think you have a few other bugs in your program. For starters, if there are multiple spaces between each word e.g.
"my gosh"
I would expect your program to print "2 4". But your code prints something else. Likely other bugs exist if there are leading or trailing spaces in your string. And the memory allocation estimate doesn't account for the extra garbage chars you are inserting in those cases.
Update:
Given that you have persevered and attempted to make a better solution in your answer below, I'm going to give you a hint. I have written a function that PRINTs the length of all words in a string. It doesn't actually allocate a string. It just prints it - as if someone had called "printf" on the string that your function is to return. Your job is to extrapolate how this function works - and then modify it to return a new string (that contains the integer lengths of all the words) instead of just having it print. I would suggest you modify the main loop in this function to keep a running total of the word count. Then allocate a buffer of size = (word_count * 4 *sizeof(int) + 1). Then loop through the input string again to append the length of each word into the buffer you allocated. Good luck.
void PrintLengthOfWordsInString(const char* str)
{
if ((str == NULL) || (*str == '\0'))
{
return;
}
while (*str)
{
int count = 0;
// consume leading white space
while ((*str) && (*str == ' '))
{
str++;
}
// count the number of consecutive non-space chars
while ((*str) && (*str != ' '))
{
count++;
str++;
}
if (count > 0)
{
printf("%d ", count);
}
}
printf("\n");
}
The answer is: it depends. There are trade-offs.
Yes, it's possible to write some extra code that, before performing this action, counts the number of words in the original string and then allocates the new string based on the number of words rather than the number of characters.
But is it worth it? The extra code would make your program longer. That is, you would have more binary code, taking up more memory, which may be more than you gain. In addition, it will take more time to run.
By the way, you have a memory leak in your program, which is more of a problem.
As long as none of the words in the sentence are longer than 9 characters, the length of your output array needs only to be the number of words in the sentence, multiplied by 2 (to account for the spaces), plus an extra one for the null terminator.
So for the string
My name is Pranay Godha
...you need only an array of length 11.
If any of the words are ten characters or more, you'll need to calculate how many extra char your array will need by determining the length of the numeric required. (e.g. a word of length 10 characters clearly requires two char to store the number 10.)
The real question is, is all of this worth it? Unless you're specifically required (homework?) to use the minimal space required in your output array, I'd be minded to allocate a suitably large array and perform some bounds checking when writing to it.

Resources