Thread: Parsing a string like I would with grep and sed

  1. #1
    Registered User
    Join Date
    Nov 2008
    Posts
    2

    Parsing a string like I would with grep and sed

    Hi,
    I wanna write some small battery-indicator in C using acpi. With bash I would simply do:

    Code:
    $ cat /proc/acpi/battery/BAT0/state                        
    present:                 yes
    capacity state:          ok
    charging state:          charged
    present rate:            0 mA
    remaining capacity:      3403 mAh
    present voltage:         15000 mV
    $ grep remain /proc/acpi/battery/BAT0/state|sed 's/^.*  //'|sed 's/mAh$//'
    3403
    But how in C? I already did:
    Code:
    $ cat bat.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(int argc, char *argv[])
    {
    	FILE *fp;
      	char str[128];
    	char value[20];
    	int i;
      	fp = fopen("/proc/acpi/battery/BAT0/state", "r");
    
      	while(fgets(str, 126, fp)) {
    		if (strstr(str,"remaining capacity:") != NULL){
    			printf("%s",str);
    		}
      	}	
    
      	fclose(fp);
      	return 0;
    }
    $ gcc -o bat bat.c
    $ ./bat
    remaining capacity:      3403 mAh
    But how can I now remove all unneeded things of this line? I just need all the numbers. So is it best to loop through the entire string char by char taking a look if the ascii-value is a number and if it is using strcat to create a new string?

    Or do you have a better solution for me?

  2. #2
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by Motark View Post
    So is it best to loop through the entire string char by char taking a look if the ascii-value is a number and if it is using strcat to create a new string?

    Or do you have a better solution for me?
    You can extract the parts of the string you want to use. Look at sscanf or strtok. There is a standard regular expression library for c (regex.h) but it's probably not necessary in this case.

    Code:
    #include <stdio.h>
    
    int main () {
    	char string[]="remaining capacity:      3403 mAh";
    	int time;
    	sscanf(string,"%*[a-z ]:%d",&time);
    	printf("%d\n",time);
    }
    The asterick tells sscanf to ignore this part of the input. Note there is a space in the square brackets, so the ignored match is for lowercase letters and spaces upto the colon.
    Last edited by MK27; 11-11-2008 at 12:05 PM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  3. #3
    Jack of many languages Dino's Avatar
    Join Date
    Nov 2007
    Location
    Chappell Hill, Texas
    Posts
    2,332
    Since C is so loose with atoi(), just pass the remaining portion of the line to it:
    Code:
    if (strstr(str,"remaining capacity:") != NULL){
    			printf("&#37;s",str);
    			printf("Value is %d\n", atoi(strchr(str,':')+1) ) ; 
    		}
    (not syntax checked)
    Last edited by Dino; 11-11-2008 at 11:40 AM.
    Mainframe assembler programmer by trade. C coder when I can.

  4. #4
    Jack of many languages Dino's Avatar
    Join Date
    Nov 2007
    Location
    Chappell Hill, Texas
    Posts
    2,332
    I guess another way would be
    Code:
    printf("Value is &#37;d\n", atoi(strpbrk(str,"0123456789")) ;
    Mainframe assembler programmer by trade. C coder when I can.

  5. #5
    Registered User
    Join Date
    Oct 2008
    Location
    TX
    Posts
    2,059
    After getting the line with strstr() you want strtok() with space as the string separator.
    Every call to strtok() gets the next token and putting it in a loop 3403 will be the 3rd token.

  6. #6
    Jack of many languages Dino's Avatar
    Join Date
    Nov 2007
    Location
    Chappell Hill, Texas
    Posts
    2,332
    I don't think so.
    Code:
    remainingBcapacity:BBBBBB3403 mAh
    3403 would be +1 AFTER the 7th token when using a blank as the token character in strtok.

    Not a good solution.
    Mainframe assembler programmer by trade. C coder when I can.

  7. #7
    Registered User
    Join Date
    Oct 2008
    Location
    TX
    Posts
    2,059
    Quote Originally Posted by Dino View Post
    I don't think so.
    Code:
    remainingBcapacity:BBBBBB3403 mAh
    3403 would be +1 AFTER the 7th token when using a blank as the token character in strtok.

    Not a good solution.
    and I humbly disagree... RTFM !!!

  8. #8
    Jack of many languages Dino's Avatar
    Join Date
    Nov 2007
    Location
    Chappell Hill, Texas
    Posts
    2,332
    Quote Originally Posted by itCbitC View Post
    and I humbly disagree... RTFM !!!
    I stand corrected, I was wrong.

    Here's the code to implement your scheme.

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(int argc, char *argv[])
    {
    	char str[] = "remaining capacity:      3403 mAh" ;
    	char * tokptr ; 
    	char delims[] = " " ; 
    	int tok_count = 0 ; 
    	
    	if (strstr(str,"remaining capacity:") != NULL){
    		printf("&#37;s\n",str);
    		
    		tokptr = strtok(str, delims) ; 
    		do { 
    			if (++tok_count==3) { 
    				printf("The value is %d\n", atoi(tokptr) ) ; 
    				break ; 
    			} 
    		} while (( tokptr = strtok(NULL, delims ))  != NULL) ; 
    	}
      	return 0;
    }
    That's so much more intuitive.
    Mainframe assembler programmer by trade. C coder when I can.

  9. #9
    Registered User
    Join Date
    Nov 2008
    Posts
    2
    Thank you very much for your ideas!!

    The sscanf solution of MK27 looks most readable so I think I'll use it

Popular pages Recent additions subscribe to a feed