Używanie printf z łańcuchem zakończonym niezerowo

107

Załóżmy, że masz ciąg, który NIE jest nullzakończony i znasz jego dokładny rozmiar, więc jak możesz wydrukować ten ciąg printfw C? Pamiętam taką metodę, ale nie mogę się teraz dowiedzieć ...

kto ja
źródło
9
W Ckontekście wszystkie ciągi są zakończone wartością null. Tablice znaków bez null w nich nie są łańcuchami ... są tablicami znaków :)
pmg
stackoverflow.com/questions/2239519/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Odpowiedzi:

174

Jest taka możliwość z printf, wygląda to tak:

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

Nie ma potrzeby kopiowania czegokolwiek, nie ma potrzeby modyfikowania oryginalnego ciągu lub bufora.

DarkDust
źródło
10
Ale w każdym razie jest to niebezpieczne, ktoś kiedyś wydrukuje ten ciąg z% s
pmod
6
@Pmod: Niekoniecznie, jeśli bufor nie jest widoczny dla świata zewnętrznego. Bardzo przydatne jest również wypisanie po prostu części łańcucha (który oczywiście może być zakończony wartością null). Jeśli naprawdę chcesz zobaczyć to w akcji, spójrz na proxy OpenSER / Kamailio SIP, gdzie często unikają kopiowania rzeczy z powodu tej techniki (również przy użyciu sprintf).
DarkDust
6
kolejne +1. Uwielbiam, kiedy uczę się podstawowych rzeczy, jak printfnawet po ~ dekadzie ... :)
Hertzel Guinness
1
Dla mnie jest to bardzo przydatne, gdy otrzymuję ciąg znaków zakończony wartością niezerową z API (niektóre API Windows to robią!) I muszę go zwrócić w "rozsądny" sposób. A więc: long life to%. * S (lub%. * S, co spowoduje również konwersję UNICODE <-> SINGLE-BYTE
!;
3
@ user1424739: W twoim przypadku printfwydrukuje do 11 znaków lub do momentu napotkania wartości NULL, w zależności od tego, co nastąpi wcześniej; w twoim przykładzie NULL jest na pierwszym miejscu. Określenie maksymalnej długości nie powoduje, że NULL traci swoje znaczenie „końca łańcucha” dla printf.
DarkDust
29

Oto wyjaśnienie, jak %.*sdziała i gdzie jest określone.

Specyfikacje konwersji w ciągu szablonu printf mają ogólną postać:

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

lub

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

Druga forma służy do uzyskania dokładności z listy argumentów:

Możesz również określić dokładność „*”. Oznacza to, że następny argument z listy argumentów (przed rzeczywistą wartością do wydrukowania) jest używany jako precyzja. Wartość musi być liczbą całkowitą i jest ignorowana, jeśli jest ujemna.

Składnia konwersji danych wyjściowych w podręczniku glibc

W przypadku %sformatowania ciągów precyzja ma specjalne znaczenie:

Można określić precyzję, aby wskazać maksymalną liczbę znaków do zapisania; w przeciwnym razie do strumienia wyjściowego zapisywane są znaki w ciągu do, ale bez kończącego znaku null.

Inne konwersje danych wyjściowych w podręczniku glibc

Inne przydatne warianty:

  • "%*.*s", maxlen, maxlen, val wyrówna do prawej, wstawiając spacje przed;
  • "%-*.*s", maxlen, maxlen, val uzasadni do lewej.
Tobu
źródło
Jeśli dobrze rozumiem, poniższe elementy będą wypełniać dane wyjściowe, ale nadal zapobiegną przepełnieniu ciągu? "%-*.*s", padding, str_view.size(), str_view.data()
scx
20

Możesz użyć fwrite () do wyjścia!

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

W ten sposób wypisujesz pierwsze znaki (liczbę zdefiniowaną w zmiennej number_of_chars) do pliku, w tym przypadku na stdout (standardowe wyjście, twój ekran)!

Pedro Sousa
źródło
2
Bardzo przydatne, gdy chcesz sprawdzić długi bufor zawierający ciągi znaków i zera!
Elist
13

printf("%.*s", length, string) nie będzie działać.

Oznacza to wydrukowanie do długości bajtów LUB bajtu zerowego, cokolwiek nastąpi wcześniej. Jeśli twoja tablica znaków zakończona wartością zerową zawiera bajty o wartości null PRZED długością, printf zatrzyma się na nich i nie będzie kontynuował.

Todd Freed
źródło
4
A jak to jest odpowiedź na pytanie PO?
Shahbaz
12
jeśli nie jest zakończony znakiem null, to null jest prawidłowym znakiem, który ma zawierać ciąg. to nadal uważa, że ​​tablica jest zakończona wartością zerową, po prostu traktuje ją jako dłuższą tablicę, z której jest wybierana podrzędnie - co oznacza, że ​​jeśli masz łańcuch zawierający wartości null, spowoduje to problemy.
lahwran
3
printf("%.5s", pointerToNonNullTerminatedString);

Długość struny będzie wynosić 5.

codeDom
źródło
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;
}

Zredagowałem kod, oto inny sposób:

#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
źródło
Bardzo zła wydajność z powodu wielu niepotrzebnych odczytów bajtów (które wiążą się ze spadkiem wydajności, jeśli bajt nie znajduje się pod adresem wyrównanym do słów na większości procesorów), a także parsowanie formatowania i stosowanie jest wykonywane dla każdego znaku. Nie rób tego :-) Zobacz moją odpowiedź na rozwiązanie.
DarkDust
@DarkDust: tylko patologiczna maszyna karałaby odczyty bajtów nie wyrównane do granic słów. Czy myślisz o odczytach słów nie wyrównanych do granic słów? Albo jakieś stare bzdury, czy coś?
R .. GitHub STOP HELPING ICE
@R ..: Jeśli uważasz, że x86 ma uszkodzony mózg i jest przestarzały, absolutnie się zgadzam. Ponieważ x86 ma kary za czytanie i zapisywanie pamięci nie wyrównanej słowami. Tak samo działa ARM. Zobacz na przykład to pytanie lub to pytanie . Chodzi o to (jeśli dobrze to zrozumiałem), że dane są pobierane z pamięci w kawałkach wielkości słowa, a pobranie prawidłowego bajtu to kolejna mikrooperacja. Nic wielkiego, ale w wielkiej pętli może to mieć znaczenie.
DarkDust
@DarkDust: całkowicie się mylisz, jeśli chodzi o odczyty bajtów. Dlaczego nie pójdziesz do testu porównawczego? x86 ma całkowicie atomowe operacje bajtowe i zawsze tak było. Nie pobiera fragmentów wielkości słowa (z wyjątkiem poziomu pamięci podręcznej, który pobiera znacznie większe fragmenty, a wyrównanie jest nieistotne, ale mówię o danych już zapisanych w pamięci podręcznej).
R .. GitHub STOP HELPING ICE
@DarkDust: PS3 nie obsługuje odczytu lub zapisu niewyrównanych bajtów na SPU. W rzeczywistości nie obsługuje nawet typów skalarnych, istnieje tylko wektor, który należy wyrównać. Kompilator je emuluje. Wiele procesorów ARM nie obsługuje odczytu ani zapisu bajtów, a jedynie wykonuje odczyt lub zapis słów.
Sylvain Defresne