Как мне заставить мою шестнадцатеричную строку включать промежуточные нули в C++?

Я пытаюсь вывести UUID, хранящийся в виде вектора байтов, в виде шестнадцатеричной строки с тире. Если какой-либо отдельный байт имеет начальный ноль, моя выходная строка пропускает его. Я использую std::setw(2) и все еще получаю такое поведение.

Я попробовал следующий код:

#include <iomanip>
#include <iostream>

std::ostream& operator<<(std::ostream &os, const Id &id) {
  std::ios oldState(nullptr);
  oldState.copyfmt(os);
  os << std::hex << std::uppercase << std::setfill('0') << std::setw(2);
  for (int i = 0; i < 16; i++) {
    os << (int)id.id_[i];
    if (i == 3 || i == 5 || i == 7 || i == 9) os << '-';
  }
  os.copyfmt(oldState);
  return os;
}

Мой ожидаемый результат будет примерно таким:

01020304-0102-0304-0506-AA0F0E0D0C0B

и вместо этого я получаю:

1234-12-34-56-AAFEDCB

Я явно делаю здесь что-то не так. Я пробовал очистить буфер и вставить тире постфактум, но все равно безуспешно.

Вам нужно вызывать setw для каждой операции вывода, для которой должна быть установлена ​​ширина (так os << std::setw(2) << static_cast<int>(id.id_[i]);). Не могу найти ни одного дубликата, который бы спрашивал об этом, вместо этого я вижу только вопросы, почему это так...

Yksisarvinen 27.09.2023 00:03

@Yksisarvinen Это помогло. Мой вывод теперь в правильном формате.

Dan 27.09.2023 00:25
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
97
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Как пояснялось в комментариях, std::setw — это одноразовый манипулятор. Он работает только со следующим выходным элементом. После этого ширина потока сбрасывается до 0.

Исправить это достаточно просто: переместите setw в цикл.

В демонстрационных целях я создал struct Id.

// main.cpp
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <vector>
#include <initializer_list>

struct Id
{
    Id(std::initializer_list<std::uint8_t> l)
        : id_{ l }
    {}
    std::vector<std::uint8_t> id_;
};

std::ostream& operator<<(std::ostream& os, const Id& id) {
    std::ios oldState(nullptr);
    oldState.copyfmt(os);
    os << std::hex << std::uppercase << std::setfill('0');
    for (int i = 0; i < 16; i++) {
        os << std::setw(2) << (int)id.id_[i];  // <===== setw goes here
        if (i == 3 || i == 5 || i == 7 || i == 9) os << '-';
    }
    os.copyfmt(oldState);
    return os;
}
int main()
{
    Id id{
        0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 
        0x05, 0x06, 0xAA, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B
    };
    std::cout << id << '\n';
    return 0;
}

Выход:

01020304-0102-0304-0506-AA0F0E0D0C0B

Я думаю, что создание функции для преобразования, а затем вызов ее в перегрузке более пригодно для повторного использования.

здесь код

#include <cstdint>
#include <iostream>
#include <array>
#include <string>

struct Id
{
    std::array<uint8_t, 16> bytes;// std::array is same as normal array but more useful feature.
};

std::string uuid2rfc4122(const Id& uuid)
{
    static char char2hex_table[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
    std::string str;
    str.reserve(36);//this prevent for reallocate memory from push_back 

    int pos = 0;
    for (auto byte : uuid.bytes) { //this intend to do not use reference. Reference is actually pointer so their size is 8 bytes in 64bit system. it has overhead if we store reference pointer instead of value.
        //1 byte represent by 2 hex character
        str.push_back(char2hex_table[byte >> 4]); //0b1111'xxxx We don't need x. After shift value = 0b1111
        str.push_back(char2hex_table[byte & 0x0f]);//0bxxxx'1111 After bitwiseAnd value = 0b0000'1111
        ++pos;
        if (pos == 4 || pos == 6 || pos == 8 || pos == 10) {
            str.push_back('-');
        }
    }
    return str;
}

std::ostream& operator<<(std::ostream& os, const Id& id) {
    os << uuid2rfc4122(id);
    return os;
}

Другие вопросы по теме