Как можно преобразовать целые числа из строки в C?
Какой самый простой способ? Мне нужна программная функция, например:
fraction create_fraction_from_string(char *str)
Где я буду в основной функции вызова файла, например:
fraction create_fraction_from_string("-12/4")
И результатом должно быть два целых числа:
int a = -12;
int b = 4;
Я думаю, что я разбиваю строку и после этого конвертирую эти две строки... Я новичок в программировании на C...
Обновлено:
дроби это:
typedef struct { int a; int b; } fraction;
И ввод всегда будет в «x/y» или «-x/y»
И в результате должно получиться:
fraction res = { .a = -12, .b = 4 };
Что такое fraction
? Это просто double
? Или это (например) typedef struct { int a; int b; } fraction;
? То есть из "-12/4"
вы хотите, чтобы результат был: double res = -3.0;
или вы хотите: fraction res = { .a = -12, .b = 4 };
? Является ли ввод всегда в форме "x/y"
или вы хотите, чтобы полный анализ уравнения (например) "-12/4*37.6+23"
?
Отвечает ли это на ваш вопрос? Как преобразовать строку в целое число в C?
@SMSamnoonAbrar Кажется, это не то, что пользователь хочет знать, потому что он действительно хочет преобразовать строку в два целых числа.
Вы можете использовать функцию sscanf
: sscanf(str, "%d/%d", &res.a, &res.b);
Стандартный подход: (1) прочитать всю строку с помощью fgets()
, (2) проанализировать то, что вам нужно, из буфера с помощью sscanf()
, как объясняет @MartinRosenau. (3) НЕ ЗАБУДЬТЕ проверить возврат ОБА fgets()
и sscanf()
и соответствующим образом обработайте любую ошибку.
Извлекая мудрость из комментариев, ваш основной подход к чтению любого ввода, где вам нужно проанализировать значения из того, что введено, заключается в следующем:
fgets()
, не экономьте на размере буфера. Подтвердите возврат, чтобы убедиться, что у вас есть действительный ввод, и пользователь не отменил ввод, создав руководство EOF
, нажав Ctrl + d (или Ctrl + z в Windows),sscanf()
. Проверьте результат, чтобы убедиться, что каждое запрошенное преобразование выполнено успешно. В противном случае обработайте ошибку.Что касается вашей функции create_fraction_from_string()
, вам нужно изменить тип возвращаемого значения, чтобы возвращаемое значение могло указывать на то, были ли преобразования из ввода успешными или неудачными. Вы можете использовать bool
для индикации true/false
или просто использовать int
с 0/1
для той же цели. Вы можете передать адрес fraction
и обновить значения по адресу указателя внутри функции, и эти значения будут доступны обратно в вызывающем объекте.
Затем в вашем вызывающем объекте (main()
здесь) подтвердите возврат от create_fraction_from_string()
, прежде чем пытаться вывести значения. Вы можете избежать UB (неопределенного поведения), если попытаетесь вывести свою дробь, полностью инициализировав структуру во время объявления. Независимо от того, используете ли вы named-initialized, как показано ниже, или просто задаете значение для каждого члена, результат будет одинаковым.
В целом, вы можете сделать что-то похожее на:
#include <stdio.h>
#define FRACFMT "%d/%d" /* if you need a constant, #define one (or more) */
#define FRACCNV 2
typedef struct {
int a, b;
} fraction;
/* fill frac from str using sscanf (str, fmt, ...), validate nconv
* conversions. Returns 1 on success, O otherwise.
*/
int create_fraction_from_string (fraction *frac, const char *str,
const char *fmt, const int nconv)
{
/* parse fmt from string into frac->a, frac->b, validate nconv */
if (sscanf (str, fmt, &frac->a, &frac->b) != nconv) {
return 0; /* return failure */
}
return 1; /* return success */
}
int main (void) {
/* BUFSIZ 4096 on Linux, 512 on Windows */
char line[BUFSIZ]; /* buffer to hold line of input */
fraction frac = { .a = 0 }; /* fraction to hold result */
fputs ("enter fraction (\"int/int\"): ", stdout); /* prompt */
if (!fgets (line, BUFSIZ, stdin)) { /* read into buffer, validate */
puts ("(user canceled input)"); /* handle manual EOF */
return 0;
}
/* call create_fraction_from_string(), validate return */
if (!create_fraction_from_string (&frac, line, FRACFMT, FRACCNV)) {
fputs ("error: invalid input, must be \"int / int\".\n", stderr);
return 1;
}
/* output result */
printf ("\nfrac.a : %d\nfrac.b : %d\n", frac.a, frac.b);
}
(примечание: вы, очевидно, должны проверить, что знаменатель не 0
в create_fraction_from_string()
- это осталось за вами)
Пример использования/вывода
$ ./bin/readfraction
enter fraction ("int/int"): -12/4
frac.a : -12
frac.b : 4
Просмотрите вещи и дайте мне знать, если у вас есть вопросы.
В чем смысл nconv
? Разве это не всегда должно быть 2?
Поскольку строка формата была определена как макрос и передана как параметр для обобщения функции синтаксического анализа, например. в форме create_X_from_string()
количество ожидаемых конверсий было передано, чтобы позволить проверке в функции сгенерировать либо успешный, либо неудачный возврат из функции. (Также позволяет избежать проблемы с MagicNumbers :)
Да, это был мой второй вопрос. Какой смысл отправлять строку формата? Такое ощущение, что единственная реальная работа, которую содержит create_fraction_from_string
, — это сообщение об ошибке.
Да, это был мыслительный процесс. Мы могли бы либо жестко закодировать ее, либо написать функцию в общем виде, с помощью которого они могли бы моделировать другие функции create_X_from_string()
. Я просто пошел с общим подходом.
Если вам нужна общая функция, то сообщение об ошибке не должно быть конкретным, верно?
Ну... В данном случае это была функция create_fraction_from_string
, но вы правы - по двум причинам (1) ошибка должна быть выведена из вызывающего объекта, чтобы разделить интерфейс и реализацию, и да (2) это можно было бы передать как параметр, но я увидел в этом сокращающиеся преимущества с точки зрения обучения. Чтобы обработать ошибку в общем виде, функция может вернуться, например. -2
, -1
, 0
для 3 разных случаев ошибок — вы можете вывести правильную ошибку, выполнив поиск в вызывающем объекте. (но когда мы доходим до этого уровня - мы начинаем терять внимание ОП :)
Да, я понимаю, что это была какая-то педагогическая причина. Тем не менее, я думаю, что это просто сделало вещи странными и трудными для понимания. Извините, если я звучу резко. Никаких обид. Если функция имеет это жестко закодированное сообщение об ошибке, это просто неправильно. И если вы также передадите сообщение об ошибке в качестве другого параметра, функция, по сути, будет просто довольно бесполезной оболочкой для sscanf
, которая практически не добавляет никакой ценности.
Если вы хотите сохранить код как есть, я могу добавить еще один ответ с более простым подходом.
Что ж, поскольку существует только одна возможная ошибка (сбой сопоставления), в этом случае мы можем передать ее обратно вызывающей стороне. (где я бы предпочел, чтобы это было в любом случае) Рад вашим мыслям и обсуждениям - я всегда считал, что 4 глаза всегда лучше, чем два, и это приводит к лучшим примерам или коду.
@David написал очень общий ответ, который может быть полезен в определенных ситуациях. Но для простоты вот более конкретный вариант:
int create_fraction_from_string (fraction *frac, const char *str)
{
const char *fmt = "%d/%d";
return (sscanf (str, fmt, &frac->a, &frac->b) == 2);
}
Это вернет 1 в случае успеха и 0 в случае неудачи, поэтому вам нужно проверить, сработало ли преобразование, прежде чем использовать frac
.
Мне нравится равенство в качестве возврата, но разве это не пришло из продвинутого класса? :)
Я не нахожу это особенно продвинутым. Наоборот, я считаю очень важным, чтобы новички изучали такие вещи как можно раньше, потому что очень часто код на языке C пишется таким образом. Тем более, что тип bool
все еще используется довольно редко, новичкам нужно знать, как обычный int
используется для логических значений.
Я просто немного поддразнил. Оба эквивалентны, один выписан, другой сделан сразу с равенством.
@DavidC.Rankin Я понял поддразнивание, но это все еще был актуальный вопрос, который, как мне казалось, заслуживал серьезного ответа. :)
Вы уже знаете, как преобразовать "-12" в -12? Как прочитать «/» в
char
? Если сначала не узнать это. Если да, то с какой проблемой вы сталкиваетесь в связи с вашей текущей целью?