Usando printf com uma string terminada não nula

107

Suponha que você tenha uma string que NÃO é nullterminada e você sabe seu tamanho exato, então como você pode imprimir essa string com printfem C? Lembro-me de tal método, mas não consigo descobrir agora ...

uau
fonte
9
No Ccontexto, todas as strings têm terminação nula. Arrays de char sem um nulo neles não são strings ... são arrays de char :)
pmg
stackoverflow.com/questions/2239519/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respostas:

174

Existe uma possibilidade com printf, é assim:

printf("%.*s", stringLength, pointerToString);

Não há necessidade de copiar nada, não há necessidade de modificar a string ou buffer original.

DarkDust
fonte
10
De qualquer forma, é perigoso, algum dia alguém imprimirá esta string com% s
pmod
6
@Pmod: Não necessariamente se o buffer não for exposto ao mundo externo. Também é muito útil imprimir apenas partes de uma string (que pode ter terminação nula, é claro). Se você realmente deseja ver isso em ação, dê uma olhada no proxy SIP OpenSER / Kamailio onde eles evitam copiar muito devido a esta técnica (também usando sprintf).
DarkDust
6
outro +1. Eu adoro quando aprendo coisas sobre coisas básicas, como printfmesmo depois de uma década ... :)
Hertzel Guinness
1
Para mim, é muito útil quando recebo uma string terminada não nula de uma API (algumas APIs do Windows fazem isso!) E tenho que retorná-la de uma forma 'razoável'. Portanto: longa vida para%. * S (ou%. * S que também fará a conversão UNICODE <-> SINGLE-BYTE para você
!;
3
@ user1424739: No seu caso printfimprimirá até 11 caracteres ou até encontrar NULL, o que ocorrer primeiro; no seu exemplo, NULL vem primeiro. Especificar um comprimento máximo não faz com que NULL perca seu significado de "fim de string" para printf.
DarkDust de
29

Aqui está uma explicação de como %.*sfunciona e onde é especificado.

As especificações de conversão em uma string de modelo printf têm a forma geral:

% [ param-no $] flags width [ . precision ] type conversion

ou

% [ param-no $] flags width . * [ param-no $] type conversion

A segunda forma é para obter a precisão da lista de argumentos:

Você também pode especificar uma precisão de '*'. Isso significa que o próximo argumento na lista de argumentos (antes do valor real a ser impresso) é usado como a precisão. O valor deve ser int e será ignorado se for negativo.

Sintaxe de conversão de saída no manual glibc

Para a %sformatação de strings, a precisão tem um significado especial:

Uma precisão pode ser especificada para indicar o número máximo de caracteres a serem escritos; caso contrário, os caracteres na string até, mas não incluindo o caractere nulo de terminação, são gravados no fluxo de saída.

Outras conversões de saída no manual glibc

Outras variantes úteis:

  • "%*.*s", maxlen, maxlen, val irá justificar à direita, inserindo espaços antes;
  • "%-*.*s", maxlen, maxlen, val irá justificar à esquerda.
Tobu
fonte
Se bem entendi, o seguinte preencheria a saída, mas ainda assim evitaria o estouro da string? "%-*.*s", padding, str_view.size(), str_view.data()
scx
20

Você pode usar um fwrite () para stdout!

fwrite(your_string, sizeof(char), number_of_chars, stdout);

Desta forma, você irá enviar os primeiros chars (número definido na variável number_of_chars) para um arquivo, neste caso para stdout (a saída padrão, sua tela)!

Pedro Sousa
fonte
2
Muito útil quando você deseja inspecionar um buffer longo contendo strings e zeros!
Elist
13

printf("%.*s", length, string) não funciona.

Isso significa imprimir até bytes de comprimento OU um byte nulo, o que ocorrer primeiro. Se o array-of-char com terminação não nula contiver bytes nulos ANTES do comprimento, printf irá parar neles e não continuará.

Todd Freed
fonte
4
E como isso é uma resposta à pergunta do OP?
Shahbaz de
12
se não tiver terminação nula, então nulo é um caractere válido para a string conter. isso ainda pensa que a matriz é terminada em nulo, apenas a trata como uma matriz mais longa da qual está sub-selecionando - o que significa que se você tiver uma string com nulos, isso causará problemas.
lahwran
3
printf("%.5s", pointerToNonNullTerminatedString);

O comprimento da string será 5.

codeDom
fonte
1
#include<string.h> 
int main()
{
/*suppose a string str which is not null terminated and n is its length*/
 int i;
 for(i=0;i<n;i++)
 {
 printf("%c",str[i]);
 }
 return 0;
}

Eu editei o código, aqui está outra maneira:

#include<stdio.h>
int main()
{
printf ("%.5s","fahaduddin");/*if 5 is the number of bytes to be printed and fahaduddin is the string.*/

return 0;

}
fuddin
fonte
Desempenho muito ruim devido a muitas leituras de bytes desnecessárias (que vêm com uma penalidade de desempenho se o byte não estiver em um endereço alinhado por palavra na maioria das CPUs) e também a análise de formatação e aplicação são feitas para cada caractere. Não faça isso :-) Veja minha resposta para a solução.
DarkDust
@DarkDust: apenas uma máquina patológica penalizaria leituras de bytes não alinhadas aos limites das palavras. Você está pensando em leituras de palavras não alinhadas aos limites das palavras? Ou alguma porcaria de mips antigos ou algo assim?
R .. GitHub PARAR DE AJUDAR O GELO
@R ..: Se você considerar o x86 com dano cerebral e desatualizado, eu concordo totalmente. Porque o x86 tem uma penalidade para leitura e gravação de memória não alinhada com palavras. O mesmo acontece com o ARM. Veja por exemplo esta questão ou esta questão . A questão é (se eu entendi isso corretamente) que os dados são buscados na memória em blocos de tamanho de palavra e obter o byte correto é outra micro-operação. Não é grande coisa, mas em um ciclo enorme pode fazer a diferença.
DarkDust
@DarkDust: você está completamente errado sobre isso para leituras de bytes. Por que você não vai fazer um benchmark? x86 tem operações de byte completamente atômicas e sempre teve. Ele não busca pedaços do tamanho da palavra (exceto no nível do cache, que busca pedaços muito maiores e o alinhamento é irrelevante, mas estou falando de dados já armazenados em cache).
R .. GitHub PARAR DE AJUDAR O GELO
@DarkDust: PS3 não oferece suporte para leitura ou gravação de bytes não alinhados no SPU. Na verdade ele nem mesmo suporta tipos escalares, existem apenas vetores, que devem ser alinhados. O compilador os emula. E muitos processadores ARM não oferecem suporte para leitura ou gravação de bytes, mas apenas para leitura ou gravação de palavras.
Sylvain Defresne