Использование sscanf для чтения строк


Я пытаюсь сохранить один символ и 2 строки в переменные. Я использую sscanf для чтения строк со следующей формой:

N "OldName" "NewName"

Чего я хочу: char character = 'N', char* old_name = "OldName", char* new_name = "NewName".

Вот как я пытаюсь это сделать:

sscanf(mystring,"%c %s %s",&character,old_name,new_name);
printf("%c %s %s",character,old_name,new_name);

Проблема в том, что моя проблема перестает работать без каких-либо выходов . (Я хочу также игнорировать кавычки и сохранить только его содержание)

3 4

3 ответа:

Когда вы это сделаете

char* new_name = "NewName";

Вы заставляете указатель new_name указывать на доступный только для чтения массив строк, содержащий постоянный строковый литерал. Массив содержит ровно 8 символов (буквы строки плюс Терминатор).

Прежде всего, использование этого указателя в качестве назначения для scanf приведет к записи scanf в массив только для чтения, что приведет к неопределенному поведению . И если вы дадите строку длиннее 7 символов, то scanf также попытается записать из границы, снова приводящие кнеопределенному поведению .

Простое решение состоит в том, чтобы использовать фактические массивы, а не указатели, а также сказать scanf, Чтобы не читать больше, чем может поместиться в массивах. Вот так:
char old_name[64];  // Space for 63 characters plus string terminator
char new_name[64];

sscanf(mystring,"%c %63s %63s",&character,old_name,new_name);

Чтобы пропустить кавычки, у вас есть несколько вариантов: либо используйте указатели и арифметику указателей, чтобы пропустить начальную кавычку, а затем установите Терминатор строки на месте последней кавычки, чтобы "удалить" ее. Другое решение состоит в том, чтобы переместить строка, чтобы перезаписать начальную цитату, а затем сделать, как в предыдущем решении, чтобы удалить последнюю цитату.

Или вы можете полагаться на ограниченные возможности сопоставления шаблоновscanf (и семья):

sscanf(mystring,"%c \"%63s\" \"%63s\"",&character,old_name,new_name);

Обратите внимание, что приведенный выше вызов sscanf будет работать ифф строка фактически включает кавычки.

Второе примечание: как сказано в комментарии Cool Guy, вышесказанное на самом деле не будет работать, так как scanf является жадным. Он будет читать: до конца файла / строки или пробела, так что он фактически не прекратит чтение при закрытии двойной кавычки. Единственным рабочим решением, использующим scanf и семейство, является следующее.

Также обратите внимание, что scanf и семейство, при чтении строки с помощью "%s" прекращает чтение на пробел, так что если строка "New Name", то она тоже не будет работать. Если это так, то вам нужно либо вручную разобрать строку, либо использовать нечетный формат "%[", что-то вроде

sscanf(mystring,"%c \"%63[^\"]\" \"%63[^\"]\"",&character,old_name,new_name);

Вы должны выделить место для ваших строк, например:

char* old_name = malloc(128); 
char* new_name = malloc(128);

Или с помощью массивов

char old_name[128] = {0};
char new_name[128] = {0};

В случае malloc вы также должны освободить место до конца вашей программы.

free(old_name);
free(new_name);

Обновлено:...

Другие ответы предоставляют хорошие методы создания памяти, а также способы считывания входных данных примера в буферы. Есть два дополнительных пункта, которые могут помочь:

1) Вы выразили, что вы тоже хотите игнорировать кавычки.
2) чтение имен и фамилий, разделенных пробелом. (пример ввода не является)

Как указывает @Joachim, поскольку scanf и семейство останавливают сканирование на пространстве со спецификатором формата %s, a имя, содержащее пробел, например "firstname фамилия", не будет прочитано полностью. Существует несколько способов решения этой проблемы. Вот два:

Способ 1: маркировка входных данных .
токенизация строка разбивает его на разделы, разделенные разделителями. Примеры ввода строк, например, разделяются по крайней мере 3-мя допустимыми разделителями: пробел: " ", двойная Цитата: ", и новая строка: \n персонажи. fgets() и strtok() можно использовать для чтения в желаемом содержании, в то же время удаляя любые нежелательные символы. Если все сделано правильно, этот метод может сохранить содержимое (даже пробелы) при удалении разделителей, таких как ". Очень простой пример концепции ниже включает следующие шаги:
1) чтение stdin в буфер строки с fgets(...)
2) проанализируйте входные данные с помощью strtok(...).

Примечание: это иллюстративный, голые кости реализация, последовательно закодированная в соответствии с вашими примерами ввода (с пробелами) и не включающая в себя проверку/обработку ошибок, которые обычно включаются.

int main(void)
{
    char line[128];
    char delim[] = {"\n\""};//parse using only newline and double quote
    char *tok;

    char letter;
    char old_name[64];  // Space for 63 characters plus string terminator
    char new_name[64];

    fgets(line, 128, stdin);
    tok = strtok(line, delim);     //consume 1st " and get token 1 
    if(tok) letter = tok[0];       //assign letter
    tok = strtok(NULL, delim);     //consume 2nd " and get token 2
    if(tok) strcpy(old_name, tok); //copy tok to old name
    tok = strtok(NULL, delim);     //consume 3rd " throw away token 3
    tok = strtok(NULL, delim);     //consume 4th " and get token 4
    if(tok) strcpy(new_name, tok); //copy tok to new name

    printf("%c %s %s\n", letter, old_name, new_name);


    return 0;
}
Примечание: как написано, Этот пример (как и большинство реализаций strtok(...)) требует очень узко определенных входных данных. В этом случае ввод должен быть не длиннее 127 символов, состоящих из одного символа, за которым следует пробел(ы), затем строка в двойных кавычках, за которой следует больше пробелов, а затем другая строка в двойных кавычках, как определено на вашем примере:
N "OldName" "NewName"

В приведенном выше примере также будут работать следующие входные данные:

N    "old name"             "new name"

N "old      name" "new        name"

Обратите внимание также на этот пример, некоторые из них считают strtok() сломанным, в то время как другие предложите избегать его использование. Я предлагаю использовать его экономно, и только в однопоточных приложениях.

Способ 2: хождение по струне .
Строка C - это просто массив char, заканчивающийся нулевым символом. Около выборочно копируя некоторые символы в другую строку, обходя при этом те, которые вам не нужны (например, "), вы можете эффективно удалить ненужные символы из вашего ввода. Вот пример функции, которая будет делать это:

 char * strip_ch(char *str, char ch) 
 {

    char *from, *to;
    char *dup = strdup(str);//make a copy of input

    if(dup)
    {
        from = to = dup;//set working pointers equal to pointer to input
        for (from; *from != '\0'; from++)//walk through input string 
        {
            *to = *from;//set destination pointer to original pointer 
            if (*to != ch) to++;//test - increment only if not char to strip
                                //otherwise, leave it so next char will replace
        }
        *to = '\0';//replace the NULL terminator
        strcpy(str, dup);
        free(dup);
    }
    return str; 
}

Пример использования:

int main(void)
{
    char line[128] = {"start"};

    while(strstr(line, "quit") == NULL)
    {
        printf("Enter string (\"quit\" to leave) and hit <ENTER>:");
        fgets(line, 128, stdin);
        sprintf(line, "%s\n", strip_ch(line, '"')); 
        printf("%s", line);
    }
    return 0;   
}