Я пытаюсь разобрать эту строку:
+CGDCONT: 0, «IP», «бесплатно», «10.158.88.34»
Жирным шрифтом выделены предметы, которые меня интересуют. Моя первая попытка заключалась в использовании этого шаблона:
"+CGDCONT: %d,\"IP\",\"%*s\",\"%d.%d.%d.%d\""
с его помощью анализируется только 1 переменная.
Затем я попробовал:
"+CGDCONT: %d,\"IP\",\"free\",\"%d.%d.%d.%d\" // the string "free" is hard coded
Это работает, но «бесплатно» не всегда может быть «бесплатно», поэтому мне нужен способ игнорировать это.
Затем я попробовал:
"+CGDCONT: %d,\"IP\",\"%s\",\"%d.%d.%d.%d\" // the string "free" is no more optionnal
с его помощью анализируются 2 переменные. Второй (%s) анализируется как
free","10.158.88.34"\8 // \8 is ascii char 8.
Как мне поступить?
Формат %s
читает строки, разделенные пробелами. Поскольку места нет, он прочитает все.
В качестве обходного пути, если вы действительно хотите использовать sscanf
, возможно, используйте \"%*[^\"]\"
. Это будет продолжаться до конца "
, но не включая его.
Если вы можете рассчитывать на то, что ввод будет достаточно регулярным, и/или если вы достаточно осторожны при написании правильного формата, проверке ошибок и проверке результатов, то вы, вероятно, можете заставить scanf
поработать для этого. Но это не совсем подходит. Вместо этого вам следует использовать регулярное выражение. Или, в зависимости от контекста, написать парсер мини-языка.
Всегда ли входная строка начинается с "+CGDCONT: "
? Всегда ли элемент "10.158.88.34"
в вашем примере будет стоять сразу после третьей запятой?
Если бы "free"
когда-либо стал, скажем, "free, but not cheap"
, я сомневаюсь, что вы смогли бы составить описатель формата scanf()
, который бы работал...
Как уже упоминалось в комментарии кого-то другого, функция sscanf
не очень подходит для этой задачи.
При условии, что
"+CGDCONT: "
итогда ты мог бы
"+CGDCONT: "
, а затем извлеките все до следующей запятой иВот пример:
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
// This function will extract the value after "+CGDCONT: " up to
// the next comma. On success, it will return true, otherwise it
// will return false.
bool extract_CGDCONT_value( const char *input, char *buffer, size_t buffer_size )
{
static const char prefix[] = "+CGDCONT: ";
static const size_t prefix_length = sizeof prefix - 1;
const char *p;
size_t num_bytes_to_copy;
// return false if input input string does not start
// with "+CGDCONT: "
if ( strncmp( input, prefix, prefix_length ) != 0 )
return false;
// make "input" point immediately after "+CGDCONT: "
input += prefix_length;
// find next comma, and return false if not found
p = strchr( input, ',' );
if ( p == NULL )
return false;
// calculate number of bytes to copy
num_bytes_to_copy = p - input;
// return false if output buffer is not large enough to store
// the result
if ( num_bytes_to_copy >= buffer_size )
return false;
// copy the result to the buffer provided by the caller
memcpy( buffer, input, num_bytes_to_copy );
// write terminating null character to buffer
buffer[num_bytes_to_copy] = '\0';
// everything went ok, so return true
return true;
}
// This function will extract everything after the third comma.
// On success, it will return true, otherwise false.
bool extract_IP_address( const char *input, char *buffer, size_t buffer_size )
{
size_t num_bytes_to_copy;
// attempt to find the third comma
for ( int i = 0; i < 3; i++ )
{
// attempt to find the next comma, and return false if
// it does not exist
input = strchr( input, ',' );
if ( input == NULL )
return false;
// make "input" point one character past the comma
input++;
}
// calculate number of bytes to copy
num_bytes_to_copy = strlen( input );
// return false if output buffer is not large enough to store
// the result
if ( num_bytes_to_copy >= buffer_size )
return false;
// copy the result to the buffer provided by the caller
strcpy( buffer, input );
// everything went ok, so return true
return true;
}
int main( void )
{
const char input[] = "+CGDCONT: 0,\"IP\",\"free\",\"10.158.88.34\"";
char CGDCONT_value[200];
char IP_address[200];
if ( extract_CGDCONT_value( input, CGDCONT_value, sizeof CGDCONT_value ) )
{
printf( "Extracted CGDCONT value: %s\n", CGDCONT_value );
}
else
{
printf( "Failed to extract CGDCONT value.\n" );
}
if ( extract_IP_address( input, IP_address, sizeof IP_address ) )
{
printf( "Extracted IP address: %s\n", IP_address );
}
else
{
printf( "Failed to extract IP address.\n" );
}
}
В этой программе жестко закодированы входные данные вопроса, и для этих входных данных она выдает следующий результат:
Extracted CGDCONT value: 0
Extracted IP address: "10.158.88.34"
@Fe2O3: Функция strrchr
должна сначала пройти от начала до конца строки, прежде чем она сможет начать поиск последней запятой, тогда как если вы ищете третью запятую с начала, вам придется пройти меньший участок строки. С другой стороны, strchr
должен сравнивать каждый символ с двумя значениями при перемещении по строке, тогда как strrchr
должен сравнивать каждый символ только с одним значением, поэтому strrchr
, вероятно, движется с большей скоростью. Кроме того, strrchr
нужно будет вызывать только один раз, а не три, поэтому накладные расходы на функцию будут меньшей проблемой.
@Fe2O3: Моя функция extract_IP_address
выполняет одну задачу — проверяет наличие трех запятых. В противном случае функция возвращает false
. Если бы я использовал strrchr
вместо strchr
, я бы не смог выполнить эту проверку ввода. Поэтому я думаю, что предпочитаю использовать strchr
. Однако вы правы в том, что использование strrchr
упростило бы код, если бы проверка ввода не требовалась.
@Fe2O3: Да, моя проверка ввода неполная. Однако, поскольку ОП не предоставил много информации о формате, я не хотел делать слишком много предположений о формате. Если мы предположим, что ввод OP имеет формат CSV и возможно, что поле содержит запятую, то мне придется добавить проверку для каждой запятой, независимо от того, заключена она в кавычки или нет.
CSV? Вероятно, есть 1 или 67 примеров вопросов и ответов по этому поводу... Ура! :-)
Если вам нужен, по сути, однострочник, вы можете сделать это следующим образом:
#include <stdio.h>
int main(void) {
char inp[] = "+CGDCONT: 0,\"IP\",\"free\",\"10.158.88.34\"";
int val = -1;
int arr[4] = { -1, -1, -1, -1 };
printf("Processing %s\n", inp);
if (sscanf(inp, " +CGDCONT: %d ,\"IP\", %*[^,], \" %d.%d.%d.%d\"",
&val, &arr[0], &arr[1], &arr[2], &arr[3]) != 5)
return 1; // handle error
printf("val = %d, arr[] = %d %d %d %d\n", val, arr[0], arr[1], arr[2], arr[3]);
}
Первая подстрока сопоставляется перед выделенным жирным шрифтом 0 или другим int
, который обрабатывается (ведущий пробел предназначен для фильтрации предыдущих символов новой строки и т. д.). Следующая подстрока совпадает, следующая пропускается с помощью %*[]
. Затем сопоставляются запятая и кавычка, за которыми следуют четыре значения int
, разделенные точкой.
Выход программы:
Processing +CGDCONT: 0,"IP","free","10.158.88.34" val = 0, arr[] = 10 158 88 34
*scanf() — неподходящий инструмент для этой работы.