Eu tenho para o formato std::string
com sprintf
e enviá-lo para fluxo de arquivo. Como posso fazer isso?
c++
string
formatting
stdstring
c++-standard-library
Max Frai
fonte
fonte
boost::format
(como a solução da kennytm usa aqui ).boost::format
já suporta operadores de fluxo C ++ também! exemplo:cout << format("helloworld. a=%s, b=%s, c=%s") % 123 % 123.123 % "this is a test" << endl;
.boost::format
tem o mínimo de linhas de código ... é revisado por pares e se integra perfeitamente aos fluxos de C ++.std::format
foi adicionado ao C ++ 20 BTW: stackoverflow.com/a/57286312/895245 Awesome!C++20
ontem e vi issoC++20
copiadoboost
(pela milionésima vez agora) adicionando ostd::format
àC++20
especificação! Eu fiquei muito, muito feliz! Quase todos os arquivos C ++ que escrevi nos últimos 9 anos foram usadosboost::format
. adicionar saída oficial do estilo printf aos fluxos em C ++ irá percorrer um longo caminho IMO para todo o C ++.Respostas:
Você não pode fazer isso diretamente, porque não possui acesso de gravação ao buffer subjacente (até C ++ 11; consulte o comentário de Dietrich Epp ). Você precisará fazer isso primeiro em uma string c, depois copiá-lo em uma std :: string:
Mas não sei por que você não usaria apenas um fluxo de string? Suponho que você tenha razões específicas para não apenas fazer isso:
fonte
char buf[100];
torna essa solução não muito robusta. Mas a ideia essencial está lá.asprintf
, que aloca uma nova string com espaço suficiente para armazenar o resultado. Em seguida, copie para um,std::string
se quiser, e lembre-sefree
do original. Além disso, é possível colocar isso em uma macro para que qualquer bom compilador irá ajudar a validar o formato para você - você não quer colocar umdouble
onde um%s
é esperadoO C ++ moderno torna isso super simples.
C ++ 20
O C ++ 20 apresenta
std::format
, o que permite fazer exatamente isso. Ele usa campos de substituição semelhantes aos do python :Confira a documentação completa ! É uma enorme melhoria na qualidade de vida.
C ++ 11
Com o C ++ 11 s
std::snprintf
, isso já se tornou uma tarefa bastante fácil e segura.O trecho de código acima está licenciado sob CC0 1.0 .
Linha por linha explicação:
Objetivo: escreva para a
char*
usandostd::snprintf
e depois converta para astd::string
.Em primeiro lugar, determina-se o comprimento desejado da matriz de char usando uma condição especial
snprintf
. Em cppreference.com :Isso significa que o tamanho desejado é o número de caracteres mais um , para que o terminador nulo fique atrás de todos os outros caracteres e que possa ser cortado pelo construtor de cadeias novamente. Esse problema foi explicado por @ alexk7 nos comentários.
snprintf
retornará um número negativo se ocorrer um erro; portanto, verificamos se a formatação funcionou como desejado. Não fazer isso pode levar a erros silenciosos ou à alocação de um buffer enorme, como apontado pelo @ead nos comentários.Em seguida, alocamos uma nova matriz de caracteres e atribuímos a a
std::unique_ptr
. Isso geralmente é recomendado, pois você não precisará manualmente manualmentedelete
novamente.Observe que essa não é uma maneira segura de alocar um
unique_ptr
com tipos definidos pelo usuário, pois você não pode desalocar a memória se o construtor lançar uma exceção!Depois disso, é claro que podemos apenas usar
snprintf
para o uso pretendido e escrever a string formatada no arquivochar[]
.Por fim, criamos e retornamos um novo
std::string
, certificando-se de omitir o terminador nulo no final.Você pode ver um exemplo em ação aqui .
Se você também deseja usar
std::string
na lista de argumentos, dê uma olhada nesta essência .Informações adicionais para o Visual Studio usuários do :
Conforme explicado nesta resposta , a Microsoft renomeou
std::snprintf
para_snprintf
(sim, semstd::
). A Microsoft ainda o define como obsoleta e recomenda o uso_snprintf_s
, mas_snprintf_s
não aceita que o buffer seja zero ou menor que a saída formatada e não calculará o comprimento das saídas, se isso ocorrer. Portanto, para se livrar dos avisos de descontinuação durante a compilação, você pode inserir a seguinte linha na parte superior do arquivo que contém o uso de_snprintf
:Pensamentos finais
Muitas respostas a essa pergunta foram escritas antes do tempo do C ++ 11 e usam comprimentos de buffer fixos ou vargs. A menos que você esteja preso às versões antigas do C ++, eu não recomendaria o uso dessas soluções. Idealmente, siga o caminho C ++ 20.
Como a solução C ++ 11 nesta resposta usa modelos, ela pode gerar um pouco de código se for usada muito. No entanto, a menos que você esteja desenvolvendo para um ambiente com espaço muito limitado para binários, isso não será um problema e ainda será uma grande melhoria em relação às outras soluções, tanto em clareza quanto em segurança.
Se a eficiência do espaço for super importante, essas duas soluções com vargs e vsnprintf podem ser úteis. NÃO USE soluções com comprimentos de buffer fixos, isso está apenas pedindo problemas.
fonte
return string(&buf[0], size);
algo parecido. Em segundo lugar, se você retornasse uma string c assim, isso causaria um comportamento indefinido porque o vetor que contém os valores para os quais você aponta será invalidado no retorno. Em terceiro lugar, quando comecei a aprender C ++, o padrão não definiu em que ordem os elementos deveriam ser armazenados dentro de umstd::vector
, portanto, acessar seu armazenamento por meio de um ponteiro era um comportamento indefinido. Agora funcionaria, mas não vejo benefício em fazê-lo dessa maneira.std::string
será construído a partir do vetor implicitamente convertido ( inicialização da cópia ), que será retornado como uma cópia, como sugere a assinatura da função. Além disso, os elementos de astd::vector
são e sempre foram destinados a serem armazenados contiguamente . Mas entendo que talvez não haja benefício em fazê-lo.return string(buf.get(), buf.get() + size);
deveria serreturn string(buf.get(), buf.get() + size - 1);
outra, você recebe uma string com um caractere nulo no final. Achei que esse fosse o caso no gcc 4.9.Solução C ++ 11 que usa
vsnprintf()
internamente:Uma abordagem mais segura e eficiente (eu testei e é mais rápida):
O
fmt_str
valor é passado em conformidade com os requisitos deva_start
.NOTA: A versão "mais segura" e "mais rápida" não funciona em alguns sistemas. Portanto, ambos ainda estão listados. Além disso, "mais rápido" depende inteiramente da etapa de pré-alocação estar correta, caso contrário, a
strcpy
torna mais lenta.fonte
size
cada iteração, quando você pode obtê-lo pela primeira chamada devsnprintf()
.boost::format()
fornece a funcionalidade que você deseja:A partir da sinopse das bibliotecas no formato Boost:
fonte
O C ++ 20 incluirá o
std::format
que se assemelhasprintf
em termos de API, mas é totalmente seguro para o tipo, funciona com tipos definidos pelo usuário e usa sintaxe de cadeia de formato semelhante ao Python. Veja como você poderá formatástd::string
-lo e gravá-lo em um fluxo:ou
Como alternativa, você pode usar a biblioteca {fmt} para formatar uma sequência e gravá-la
stdout
ou transmitir um arquivo de uma só vez:Quanto à
sprintf
maioria das outras respostas aqui, infelizmente, elas usam varargs e são inerentemente inseguras, a menos que você use algo como oformat
atributo do GCC, que só funciona com strings de formato literal. Você pode ver por que essas funções não são seguras no seguinte exemplo:onde
string_format
está uma implementação da resposta do Erik Aronesty. Esse código é compilado, mas provavelmente trava quando você tenta executá-lo:Isenção de responsabilidade : sou o autor de {fmt} e C ++ 20
std::format
.fonte
error: 'fmt' has not been declared
fmt
implementação semelhante foi adicionada ao C ++ 20! O stackoverflow.com/a/57286312/895245 fmt atualmente reivindica suporte para ele. Otimo trabalho!Se você deseja apenas uma sintaxe do tipo printf (sem precisar chamar o printf), consulte o Boost Format .
fonte
Eu escrevi meu próprio usando vsnprintf, para que ele retorne uma string em vez de ter que criar meu próprio buffer.
Então você pode usá-lo como
fonte
vsnprintf
diretamente na string.std::unique_ptr<char[]> buffer (new char[size]);
Para formatar
std::string
de forma 'sprintf', chamesnprintf
(argumentosnullptr
e0
) para obter o tamanho do buffer necessário. Escreva sua função usando o modelo variável C ++ 11 como este:Compile com suporte ao C ++ 11, por exemplo, no GCC:
g++ -std=c++11
Uso:
fonte
char buf[length + 1];
vez dechar* buf = new char[length + 1];
?char[]
echar*
com o novo é que, no primeiro caso, o buf seria alocado na pilha. É bom para buffers pequenos, mas como não podemos garantir o tamanho da string resultante, é um pouco melhor usá-lonew
. Por exemplo na minha máquinastring_sprintf("value: %020000000d",5)
, imprimir o número exorbitante de zeros antes do número 5, core dumps quando usando um array na pilha, mas funciona bem quando se utiliza alocada dinamicamente variedadenew char[length + 1]
std::move
é supérfluo .[edit: 20/05/25] melhor ainda ...:
No cabeçalho:
A
PRINTSTRING(r)
função-é atender à GUI ou ao terminal ou a qualquer saída especial que seja necessária#ifdef _some_flag_
, o padrão é:[edit '17 / 8/31] Adicionando uma versão com modelo variável 'vtspf (..)':
que é efetivamente uma versão delimitada por vírgula (em vez) dos
<<
operadores às vezes dificultadores , usados assim:[editar] Adaptado para fazer uso da técnica na resposta de Erik Aronesty (acima):
[resposta anterior]
Uma resposta muito tardia, mas para quem, como eu, gosta do jeito 'sprintf': escrevi e estou usando as seguintes funções. Se você gostar, poderá expandir as opções%-para se ajustarem melhor às opções do sprint; os que estão lá atualmente são suficientes para minhas necessidades. Você usa stringf () e stringfappend () da mesma forma que usaria o sprintf. Lembre-se de que os parâmetros para ... devem ser do tipo POD.
fonte
fmt
como referência, funciona bem. (Mas suponho que as pessoas vão querer brincar com brinquedos, então ...) Se houver outros supostos 'bugs', você poderia elaborar?É assim que o Google faz:
StringPrintf
(Licença BSD)e o Facebook fazem de maneira bastante semelhante:
StringPrintf
(Licença Apache)Ambos fornecem um conveniente
StringAppendF
também.fonte
Meus dois centavos nesta questão muito popular.
Para citar a página de manual de
printf
funções like :Em outras palavras, uma implementação sã do C ++ 11 deve ser a seguinte:
Funciona muito bem :)
Modelos variáveis são suportados apenas no C ++ 11. A resposta do pixelpoint mostra uma técnica semelhante usando estilos de programação mais antigos.
É estranho que o C ++ não tenha essa coisa pronta para uso. Eles recentemente acrescentaram
to_string()
, o que, na minha opinião, é um grande passo à frente. Gostaria de saber se eles irão adicionar um.format
operador para ostd::string
eventualmente ...Editar
Como alexk7 apontou, A
+1
é necessário no valor de retorno destd::snprintf
, pois precisamos ter espaço para o\0
byte. Intuitivamente, na maioria das arquiteturas que faltam+1
, orequired
número inteiro será parcialmente substituído por a0
. Isso acontecerá após a avaliação derequired
como parâmetro real parastd::snprintf
, portanto, o efeito não deve ser visível.No entanto, esse problema pode mudar, por exemplo, com a otimização do compilador: e se o compilador decidir usar um registro para a
required
variável? Esse é o tipo de erro que às vezes resulta em problemas de segurança.fonte
bytes
buffer, provavelmente sobre orequired
número inteiro (que felizmente nesse ponto já está avaliado).nullptr
argumento como buffer, eliminando achar b;
linha no seu código. ( Fonte )Usando C99 snprintf e C ++ 11
fonte
Testado, resposta de qualidade da produção
Esta resposta lida com o caso geral com técnicas compatíveis com os padrões. A mesma abordagem é dada como um exemplo no CppReference.com, na parte inferior da página. Ao contrário do exemplo, esse código se encaixa nos requisitos da pergunta e é testado em campo em robótica e aplicações de satélite. Também melhorou os comentários. A qualidade do design é discutida mais adiante.
Eficiência Linear Previsível
Duas passagens são necessárias para uma função reutilizável segura, confiável e previsível, de acordo com as especificações da pergunta. Pressupostos sobre a distribuição de tamanhos de vargs em uma função reutilizável é um estilo de programação ruim e devem ser evitados. Nesse caso, representações arbitrariamente grandes de comprimento variável de vargs são um fator-chave na escolha do algoritmo.
Tentar novamente com estouro excessivo é exponencialmente ineficiente, que é outro motivo discutido quando o comitê de padrões do C ++ 11 discutiu a proposta acima para fornecer uma execução a seco quando o buffer de gravação é nulo.
Na implementação pronta para produção acima, a primeira execução é uma execução tão seca para determinar o tamanho da alocação. Nenhuma alocação ocorre. A análise das diretivas printf e a leitura de vargs tornaram-se extremamente eficientes ao longo de décadas. O código reutilizável deve ser previsível, mesmo que uma pequena ineficiência para casos triviais deva ser sacrificada.
Segurança e Confiabilidade
Andrew Koenig disse a um pequeno grupo de nós após sua palestra em um evento de Cambridge: "As funções do usuário não devem contar com a exploração de uma falha por funcionalidades não excepcionais". Como sempre, sua sabedoria se mostrou verdadeira desde então. Problemas de bug de segurança corrigidos e fechados geralmente indicam novas tentativas de invasão na descrição do furo explorado antes da correção.
Isso é mencionado na proposta formal de revisão de padrões para o recurso de buffer nulo em Alternativa ao sprintf, Proposta de revisão C9X , Documento ISO IEC WG14 N645 / X3J11 96-008 . Uma cadeia arbitrariamente longa inserida por diretiva de impressão, "% s", dentro das restrições de disponibilidade de memória dinâmica, não é uma exceção e não deve ser explorada para produzir "Funcionalidade excepcional".
Considere a proposta ao lado do código de exemplo fornecido na parte inferior da página do C ++ Reference.org vinculado no primeiro parágrafo desta resposta.
Além disso, o teste de casos de falha raramente é tão robusto em casos de sucesso.
Portabilidade
Todos os principais fornecedores de sistemas operacionais fornecem compiladores que suportam totalmente o std :: vsnprintf como parte dos padrões do c ++ 11. Os hosts que executam produtos de fornecedores que não mantêm mais distribuições devem ser fornecidos com g ++ ou clang ++ por vários motivos.
Uso da pilha
O uso da pilha na 1ª chamada para std :: vsnprintf será menor ou igual ao da 2ª e será liberado antes do início da 2ª chamada. Se a primeira chamada exceder a disponibilidade da pilha, o std :: fprintf também falhará.
fonte
C ++ 20
std::format
Chegou! O recurso é descrito em: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0645r9.html e usa uma
.format()
sintaxe semelhante ao Python .Espero que o uso seja como:
Vou tentar quando o suporte chegar ao GCC, o GCC 9.1.0 com
g++-9 -std=c++2a
ainda não o suporta.A API adicionará um novo
std::format
cabeçalho:A
fmt
biblioteca existente afirma implementá-lo se você precisar do polyfill: https://github.com/fmtlib/fmte foi mencionado anteriormente em: std :: string formatating like sprintf
fonte
Com base na resposta fornecida por Erik Aronesty:
Isso evita a necessidade de se afastar
const
do resultado.c_str()
que estava na resposta original.fonte
fonte
_vscprintf
é. Eu acho que você deveria elaborar esta resposta.string não tem o que você precisa, mas std :: stringstream possui. Use um stringstream para criar a string e depois extraí-la. Aqui está uma lista abrangente das coisas que você pode fazer. Por exemplo:
fornecerá 10 casas decimais de precisão ao imprimir um double ou float.
fonte
Você pode tentar o seguinte:
fonte
Se você estiver em um sistema que possua asprintf (3) , poderá envolvê-lo facilmente:
fonte
format
, como diz gcc para verificar os tipos de argumentos e dar um aviso decente com -Wall:std::string format(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
va_end
. "Se va_end não é chamado antes de uma função que chama va_start ou retorna va_copy, o comportamento é indefinido." - docsthrow std::bad_alloc();
, pois não estou usando exceções de C ++ na minha base de código e, para as pessoas que o fazem, podem adicioná-lo facilmente com base no comentário de origem e seu comentário aqui.Este é o código que eu uso para fazer isso no meu programa ... Não é nada chique, mas faz o truque ... Nota, você terá que ajustar seu tamanho conforme aplicável. MAX_BUFFER para mim é 1024.
fonte
vsnprintf
diretamente na string.Tomou a idéia da resposta de Dacav e pixelpoint . Eu brinquei um pouco e consegui isso:
Com a prática de programação sã, acredito que o código deve ser suficiente, no entanto, ainda estou aberto a alternativas mais seguras que ainda são simples o suficiente e não exigem C ++ 11.
E aqui está outra versão que utiliza um buffer inicial para impedir a segunda chamada
vsnprintf()
quando o buffer inicial já é suficiente.(Acontece que esta versão é semelhante à resposta de Piti Ongmongkolkul , apenas que ela não usa
new
edelete[]
, e também especifica um tamanho ao criarstd::string
.A idéia aqui de não usar
new
edelete[]
é implicar o uso da pilha sobre o heap, pois ele não precisa chamar funções de alocação e desalocação; no entanto, se não for usado corretamente, pode ser perigoso armazenar buffer em alguns (talvez antigos ou talvez apenas vulneráveis). Se isso é uma preocupação, eu sugiro usarnew
e emdelete[]
vez disso. Observe que a única preocupação aqui é sobre as alocações, comovsnprintf()
já é chamado com limites, portanto, especificar um limite com base no tamanho alocado no segundo buffer também os impediria.)fonte
Eu costumo usar isso:
Desvantagem: nem todos os sistemas suportam vasprint
fonte
Abaixo, a versão ligeiramente modificada da resposta @iFreilicht, atualizada para C ++ 14 (uso da
make_unique
função em vez da declaração bruta) e suporte adicional parastd::string
argumentos (com base no artigo de Kenny Kerr )Resultado:
Sinta-se à vontade para mesclar esta resposta com a original, se desejar.
fonte
A biblioteca Poco Foundation possui uma função de formato muito conveniente, que suporta std :: string na string de formato e nos valores:
fonte
Você pode formatar a saída C ++ no cout usando o arquivo de cabeçalho iomanip. Certifique-se de incluir o arquivo de cabeçalho iomanip antes de usar qualquer uma das funções auxiliares, como setprecision, setfill etc.
Aqui está um trecho de código que eu usei no passado para imprimir o tempo médio de espera no vetor, que eu "acumulei".
Aqui está uma breve descrição de como podemos formatar fluxos C ++. http://www.cprogramming.com/tutorial/iomanip.html
fonte
Pode haver problemas, se o buffer não for grande o suficiente para imprimir a sequência. Você deve determinar o comprimento da sequência formatada antes de imprimir uma mensagem formatada. Eu faço próprio auxiliar para isso (testado no Windows e Linux GCC ), e você pode tentar usá-lo.
String.cpp: http://pastebin.com/DnfvzyKP
String.h: http://pastebin.com/7U6iCUMa
String.cpp:
String.h:
fonte
vsnprintf((char *)dst.data(), dst.size() + 1, format, ap);
- É seguro assumir que o buffer da string tem espaço para um caractere nulo final? Existem implementações que não alocam tamanho + 1 caracteres. Seria mais seguro fazer #dst.resize(length+1); vsnprintf((char *)dst.data(), dst.size(), format, ap); dst.resize(length);
data
ec_str
é sinônimo.fonte
Solução muito, muito simples.
fonte
Sei que isso foi respondido muitas vezes, mas isso é mais conciso:
exemplo:
Veja também http://rextester.com/NJB14150
fonte
ATUALIZAÇÃO 1 : adicionada
fmt::format
testesEu levei minha própria investigação em torno dos métodos introduzidos aqui e obtive resultados diametralmente opostos versus os mencionados aqui.
Eu usei 4 funções em 4 métodos:
vsnprintf
+std::unique_ptr
vsnprintf
+std::string
std::ostringstream
+std::tuple
+utility::for_each
fmt::format
função dafmt
bibliotecaPara o back-end de teste, o
googletest
usado.A
for_each
implementação é retirada daqui: itere sobre a tuplaOs testes:
o
UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR
.unsued.hpp :
unused.cpp :
RESULTADOS :
Como você pode ver, a implementação pelo
vsnprintf
+std::string
é igual afmt::format
, mas mais rápida que pelovsnprintf
+std::unique_ptr
, que é mais rápido que pelostd::ostringstream
.Os testes foram compilados
Visual Studio 2015 Update 3
e executados emWindows 7 x64 / Intel Core i7-4820K CPU @ 3.70GHz / 16GB
.fonte