Я не фанат использования scanf()
et.al.для анализа данных, так как простой scanf("%d,%d")
склонен к ошибкам при различном вводе пользователем.
Мой общий подход при работе с известными символами форматирования (такими как (
, ,
, )
),это сначала найти их с помощью strchr()
, подтвердить, что они несколько разумны, и только затем попытаться извлечь значение.
В приведенном ниже коде я нахожу скобки и запятую, затем копирую возможно числовые данные между ними, прежде чем передать их strtol()
для преобразования целочисленной строки в числовое представление.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_NUMBER_LEN 32
/*
** Given a string which contains (somewhere) a pair
** of numbers in the form "... (x, y) ...", parse the pair
** into val1 and val2 respectively.
**
** Returns the point at which the input ended successfully
** or NULL on error
*/
const char *parseTuple(const char *input, int *val1, int *val2)
{
char *result = NULL;
char val1_str[ MAX_NUMBER_LEN+1 ] = { '\0' };
char val2_str[ MAX_NUMBER_LEN+1 ] = { '\0' };
// Find the first '('
char *left_paren = strchr( input, '(' );
char *right_paren = strchr( input, ')' );
char *comma = strchr( input, ',' );
// validate the things we found exist, and are in valid positions
if ( left_paren != NULL && right_paren != NULL && comma != NULL && // needed parts exist
left_paren < comma && comma < right_paren ) // in the correct order
{
// val1 source string exists between left_paren+1 and comma-1
int val1_len = comma-1 - left_paren+1 - 1;
if ( val1_len > 0 && val1_len < MAX_NUMBER_LEN )
{
strncpy( val1_str, left_paren+1, val1_len );
val1_str[ val1_len ] = '\0';
}
// val2 source string exists between comma+1 and right_paren-1
int val2_len = right_paren-1 - comma+1 - 1;
if ( val2_len > 0 && val2_len < MAX_NUMBER_LEN )
{
strncpy( val2_str, comma+1, val2_len );
val2_str[ val2_len ] = '\0';
}
// If we extracted some reasonable numbers, try to parse them
if ( val1_str[0] != '\0' && val2_str[0] != '\0' )
{
*val1 = strtol( val1_str, NULL, 10 );
*val2 = strtol( val2_str, NULL, 10 );
// TODO handle errno when string is not a number
// if errono did not indicate a strol() failure
result = right_paren+1; // point to the next input location, so we can call again
}
}
return result;
}
int main(int argc, char **argv)
{
const char *input;
int val1;
int val2;
for (int i=1; i<argc; i++)
{
input = argv[i];
do
{
printf( "From input of: [%s]\n" , input );
input = parseTuple( input, &val1, &val2 );
if ( input != NULL )
printf( " Parsed out: (%3d,%3d)\n", val1, val2 );
} while ( input != NULL && strlen( input ) );
}
return 0;
}
Предоставление тестового прогона:
$ ./parse_tuple '(-3, 2)' '(1,1)(11111111111111111111111111111111111111111111111111111111111111111111,0) () (,)' '(' '()' ')' '(,)' '(-12,)' '(123,456)'
From input of: [(-3, 2)]
Parsed out: ( -3, 2)
From input of: [(1,1)(11111111111111111111111111111111111111111111111111111111111111111111,0) () (,)]
Parsed out: ( 1, 1)
From input of: [(11111111111111111111111111111111111111111111111111111111111111111111,0) () (,)]
From input of: [(]
From input of: [()]
From input of: [)]
From input of: [(,)]
From input of: [(-12,)]
From input of: [(123,456)]
Parsed out: (123,456)