Стандартные функции преобразования строк C/C++ не поддерживают литералы двоичных строк?C++

Программы на C++. Форум разработчиков
Ответить
Anonymous
 Стандартные функции преобразования строк C/C++ не поддерживают литералы двоичных строк?

Сообщение Anonymous »

Существуют ли какие-либо стандартные функции C или C++, которые автоматически определяют двоичную строку, например «0b1010» и преобразуют ее в целое число 10?
Просто пытаюсь подтвердить то, что кажется некоторым упущением в стандартах C и C++. Последние версии обоих поддерживают двоичные целочисленные литералы (), но различные функции преобразования строк этого не делают при автоматическом определении базы. Хотя технически это не связано, асимметрия кажется мне упущением.
Целочисленные литералы
Начиная с C++14 и C23, C и C++ поддерживают следующие общие форматы целочисленных литералов[1]:



Базовое число
Префикс
Пример
Стандартное




Десячное число (основание 10)
без префикса

Восьмеричная (основание 8)

Шестнадцатеричный (с основанием 16) или 0X

Двоичный (основание 2) или 0B Начиная с C23 и C++14



Поэтому в своем коде вы можете написать что-то вроде int x = 0xAB; или int y = 0b11;.
Разбор строк
Теперь предположим, что мы хотим преобразовать несколько строк в числа, например "123", "0755", "0x7F" и "0b1010". К счастью для нас, C и C++ предоставляют множество возможностей, таких как:
  • Семейство strtol[2] поскольку (по крайней мере) C99 и C++98 (или C++11 для вариантов ll)
  • Семейство std::stol[2] поскольку С++11
  • Код: Выделить всё

    std::from_chars
    по крайней мере C++17.
Хотя std::from_chars требует указания базы больше 2, семейства функций strtol и stol допускают базу 0 и позволяют функциям автоматически определять базу. Вот описание из документации на C strtol, выделенное для акцента:

strtol

Код: Выделить всё

long  strtol( const char* restrict str, char** restrict str_end, int base );
(начиная с C99)
Интерпретирует целочисленное значение в байтовой строке, на которую указывает str.
Отбрасывает любые пробельные символы (определяемые вызовом isspace) до тех пор, пока не будет найден первый непробельный символ, затем берет как можно больше символов для формирования допустимого представления целочисленного числа по основанию n (где n = основание) и преобразует их в целочисленное значение. Допустимое целочисленное значение состоит из следующих частей:
  • (необязательно) знак плюс или минус
  • (необязательно) префикс (0), указывающий восьмеричную базу (применяется только тогда, когда основа равна 8 или ​0​)
  • (необязательно) префикс (0x или 0X), указывающий шестнадцатеричную базу (применяется только тогда, когда база равна 16 или ​0​)
  • последовательность цифр
Набор допустимых значений для базы — {0, 2, 3, ..., 36}. Набор допустимых цифр для целых чисел с основанием 2 — это {0, 1}, для целых чисел с основанием 3 — {0, 1, 2} и так далее. Для чисел с основанием больше 10 допустимые цифры включают буквенные символы, начиная с Aa для целого числа с основанием 11 до Zz для целого числа с основанием 36. Регистр символов игнорируется.
Дополнительные числовые форматы могут быть приняты установленной в данный момент локалью C.
Если значение базы равно ​0​, числовая база определяется автоматически: если префикс равен 0, база является восьмеричной, если префикс 0x или 0X, база является шестнадцатеричной, в противном случае база – десятичное.

На справочной странице для std::stol указано, что он вызывает strtol внутри, поэтому его поведение фактически идентично.
Асимметрия преобразования
И все семейства функций strtol и std::stol были добавлены в стандарты C и C++ в C99 и C++14, соответственно, и до включения двоичных целочисленных литералов в C23 и C++17.
Кроме того, в C++ теперь есть std::format, начиная с C++20, а спецификация стандартного формата поддерживает двоичный формат b, например {:b. ~~Интересно, что printf не поддерживает сопоставимый тип формата %b. (В ссылке ничего не указано)~~ EDIT: Неважно, похоже, что по крайней мере начиная с C23 printf действительно поддерживает %b. См. §7.23.6.1 n3220. Не знаю, когда он был добавлен, поскольку я не проверял предыдущие стандарты C или C++.
Это означает, что вы можете столкнуться со следующей асимметрией:

Код: Выделить всё

#include 
#include 
#include 
#include 

int main() {
unsigned bin = 0b1010; // 10
unsigned oct = 0755;   // 493
unsigned dec = 123;    // 123
unsigned hex = 0x7F;   // 127

auto text = std::format("0b{:b}, 0{:o}, {:d}, 0x{:x}", bin, oct, dec, hex);
puts(text.c_str());

char str[] = "0b1010 0755 123 0x7F";
char* token = strtok(str, " ");
while (token) {
unsigned val = strtoul(token, nullptr, 0);
printf("parsed string '%s' as integer '%d'\n", token, val);

token = strtok(nullptr, " ");
}

return 0;
}
Вывод программы[3] ниже. Обратите внимание, что "0b1010" неправильно анализируется как 0!

Код: Выделить всё

0b1010, 0755, 123, 0x7f
parsed string '0b1010' as integer '0'
parsed string '0755' as integer '493'
parsed string '123' as integer '123'
parsed string '0x7F' as integer '127'
Я знаю, что могу написать специальный случай для обнаружения «0b», но это раздражает, и я хотел посмотреть, есть ли какая-нибудь стандартная функция в C или C++, которую мне не хватает?
Примечания
[1] Технически это «целочисленные константы» в C и «целочисленные литералы» в C++, но для этого это не важно вопрос.
[2] Для краткости я рассматриваю strtol, strtoll, strtoul и strtoull как одно и то же. За исключением типа возвращаемого значения и различий со знаком/без знака, в остальном они идентичны. Аналогично для std::stoi, std::stol, std::stoll, std::stoul и std::stoull.
[3] https://godbolt.org/z/EETe1eYGz

Подробнее здесь: https://stackoverflow.com/questions/798 ... g-literals
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C++»