Использование 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 ответа:
Когда вы это сделаете
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; }