У меня есть несколько чисел разной длины (например, 1, 999, 76492 и т. д.), И я хочу преобразовать их все в строки с общей длиной (например, если длина равна 6, то эти строки будут: '000001' , 000999, 076492).
Другими словами, мне нужно добавить к числу правильное количество ведущих нулей.
int n = 999;
string str = some_function(n,6);
//str = '000999'
Есть ли такая функция в C++?





Есть много способов сделать это. Самый простой:
int n = 999;
char buffer[256]; sprintf(buffer, "%06d", n);
string str(buffer);
Буфер размером 256 является излишним путь для этой цели. Несмотря на то, что число может превышать 7 символов (с чем связан ответ Исака с помощью snprintf), тем не менее, ни один известный мне int не занимает 256 символов. :-П
Хорошее замечание, Крис. Я думаю, что самый большой int на самом деле чуть больше 999 999 999 999 999, поэтому char buffer [17] совершенно безопасен, учитывая \ 0 и, возможно, знак (хотя я не думаю, что самый большой отрицательный int будет где-то рядом с таким длинным).
Самый большой int зависит от реализации. Большинство современных компиляторов используют 32-битное двоичное дополнение int, что означает, что наибольший int равен 2 147 483 647, а самый длинный int - min int, -2 147 483 648. Это 12 символов, считая завершающий NUL. Для переносимого кода int может иметь ЛЮБУЮ длину.
Во-первых, это не С ++, а самый большой int - 2147483648, длина которого составляет всего 10 символов. Откуда у вас 999 999 999 999 999, я понятия не имею. Без знака он больше, но все равно имеет то же количество символов.
@graham: для целого числа со знаком вы также должны учитывать знак. Самый длинный номер - 2147483648, состоит из 11 символов (не считая нулевого байта).
Несмотря на предполагаемую простоту, sprintf - плохое решение. snprintf или stringstream предпочтительнее.
Пространство стека можно выделить бесплатно, и если вы собираетесь переполнить стек только потому, что вы выделили буфер размером 256 байт, у вас большие проблемы. Хотя snprintf действительно безопаснее, он есть только в C99. Stringstream должен будет выделить некоторое количество памяти под капотом, поэтому он немного медленнее.
Я думаю, вы были отвергнуты пуристами C++ с большим «лабораторным» опытом ... у вас все было хорошо, пока кто-то не упомянул omfgstringstreams.
@Pramod: он все равно хочет строку в конце, поэтому там требуется выделение памяти. Я подозреваю (но не могу позаботиться о том, чтобы убедиться в этом) реализации STL не будут выделять больше памяти в решении xtofl, чем в вашем. Ваш просто сохраняет его на последнюю строчку.
sprintf - это способ, подобный C, который также работает в C++.
В C++ комбинация строкового потока и форматирования вывода потока (см. http://www.arachnoid.com/cpptutor/student3.html) выполнит свою работу.
char str[7];
snprintf (str, 7, "%06d", n);
См. snprintf
Хотя рекомендуется всегда использовать snprintf (), это одно из немногих мест, где вы можете безопасно использовать sprintf ().
Да, знаю. Но я всегда использую snprintf, потому что на самом деле нет причин не делать этого (разница в производительности незначительна).
arg # 2 - это size_t, а не len, поэтому это 7, а не 6. Однако лучше использовать sizeof.
Не уверен, что мне что-то не хватает, но я думаю, что ему также нужен тип в строке формата, поэтому "% 06d" или аналогичный
Первым аргументом snprintf должен быть str, а не buf.
или используя строковые потоки:
#include <sstream>
#include <iomanip>
std::stringstream ss;
ss << std::setw(10) << std::setfill('0') << i;
std::string s = ss.str();
Я собрал информацию, которую нашел на arachnoid.com, потому что мне больше нравится типобезопасный способ iostreams. Кроме того, вы можете использовать этот код в любом другом потоке вывода.
К вашему сведению - для этого требуются включаемые файлы sstream и iomanip.
И stringstream, setw и setfill (а также string) находятся в пространстве имен std.
В подобных ответах я всегда показываю результат. В данном случае, если int i=1;, это 0000000001.
К вашему сведению, вы должны написать std::setfill('0'), как это делает этот ответ, вместо std::setfill("0"). (Я использую C++ 11)
Что делать, если i больше 10 цифр?
stringstream будет делать (как указал xtofl). Формат повышения - более удобная замена snprintf.
Этот метод не использует ни потоки, ни sprintf. Помимо проблем с блокировкой, потоки несут накладные расходы на производительность и действительно являются излишними. Для потоков накладные расходы возникают из-за необходимости создания буфера пара и потока. Для sprintf накладные расходы возникают из-за необходимости интерпретировать строку формата. Это работает, даже когда п отрицательно или когда строковое представление п длиннее, чем len. Это САМОЕ БЫСТРОЕ решение.
inline string some_function(int n, int len)
{
string result(len--, '0');
for (int val=(n<0)?-n:n; len>=0&&val!=0; --len,val/=10)
result[len]='0'+val%10;
if (len>=0&&n<0) result[0]='-';
return result;
}
Одна вещь, о которой вы должны знать май, - это потенциальная блокировка, которая может происходить, когда вы используете подход stringstream. В STL, который поставляется с Visual Studio 2008, по крайней мере, есть много снятых и выпущенных блокировок, поскольку во время форматирования используется различная информация о локали. Это может быть, а может и не быть проблемой для вас, в зависимости от того, сколько у вас потоков, которые могут одновременно преобразовывать числа в строки ...
Версия sprintf не принимает никаких блокировок (по крайней мере, в соответствии с инструментом мониторинга блокировок, который я разрабатываю в данный момент ...), и поэтому может быть «лучше» для использования в параллельных ситуациях.
Я заметил это только потому, что мой инструмент недавно заявил, что блокировки «локали» являются одними из наиболее часто используемых для блокировок в моей серверной системе; это стало немного неожиданностью и может заставить меня пересмотреть подход, который я использовал (т. е. вернуться к sprintf от stringstream) ...
Имеет смысл использовать локаль, но на самом деле она заблокирована ... Ценная информация!
Это может быть только Visual Studio STL, я не проверял тестовую программу, созданную с помощью STLPort. Я также не исследовал, почему он блокируется.
STLPort 5.1.5 не демонстрирует эту проблему конкуренции между потоками, но стиль преобразования sprintf все еще примерно в 3 раза быстрее ...
Вот интересный пост Лена Холгейта: lenholgate.com/archives/000824.html
Это старый поток, но поскольку fmt может стать стандартом, вот дополнительное решение:
#include <fmt/format.h>
int n = 999;
const auto str = fmt::format("{:0>{}}", n, 6);
Обратите внимание, что fmt::format("{:0>6}", n) работает одинаково хорошо, когда желаемая ширина известна во время компиляции. Другой вариант - спуск:
#include <absl/strings/str_format.h>
int n = 999;
const auto str = absl::StrFormat("%0*d", 6, n);
Опять же, abs::StrFormat("%06d", n) возможен. формат повышения - еще один инструмент для решения этой проблемы:
#include <boost/format.hpp>
int n = 999;
const auto str = boost::str(boost::format("%06d") % n);
К сожалению, спецификатор переменной ширины в качестве аргументов, связанных с оператором %, не поддерживается, для этого требуется настройка строки формата (например, const std::string fmt = "%0" + std::to_string(6) + "d";).
Что касается производительности, abseil и fmt утверждают, что они очень привлекательны и быстрее, чем boost. В любом случае все три решения должны быть более эффективными, чем подходы std::stringstream, и, за исключением семейства std::*printf, они не жертвуют типовой безопасностью.
в этом случае вы действительно можете захотеть использовать: sprintf (buffer, "% 06d", n); обратите внимание на 0 перед 6, которое вам нужно дополнить нулями