printf string formatting

The power of sprintf will rarely let you down when it comes to structuring various types of data into strings. Since sprintf is almost the same as printf in usage, only the destination of printing is different. The former is printed to a string, and the latter is output directly on the command line. This also makes sprintf much more useful than printf.

sprintf is a variable parameter function, which is defined as follows: 
int sprintf( char *buffer, const char *format [, argument] ... ); In addition to the fixed types of the first two parameters, any number of parameters can be followed. And its essence is obviously in the second parameter: the format string.

Both printf and sprintf use a format string to specify the format of the string. Inside the format string, some format specifiers (format specifications) beginning with "%" are used to occupy a position, and the corresponding variables are provided in the following variable parameter list. , the final function will replace that specifier with the variable in the corresponding position, producing a string that the caller wants.

1. Formatting numeric strings


One of the most common applications of sprintf is to print integers to strings, so spritnf can replace itoa in most cases.

Such as: 
//Print the integer 123 as a string and save it in s. 
sprintf(s, "%d", 123); //Generate "123" to specify the width, fill space on the left side of the shortage: 
sprintf(s, "%8d%8d", 123, 4567); //Generate: "123 4567" of course can also be left-aligned: 
sprintf(s, "%-8d%8d", 123, 4567); // yields: "123 4567"

It can also be printed in hexadecimal: 
sprintf(s, "%8x", 4567); //lowercase hexadecimal, width occupies 8 positions, right-aligned 
sprintf(s, "%-8X", 4568); // Uppercase hexadecimal, 8 positions wide, left-aligned

In this way, an integer hexadecimal string is easy to get, but when we print hexadecimal content, we usually want a monospaced format with left-padded 0s, so what should we do? It's as simple as adding a 0 in front of the number representing the width. 
sprintf(s, "%08X", 4567); //Generate: "000011D7" 
The decimal printing with "%d" above can also use this method of padding 0 on the left.

There is a sign extension problem to pay attention to here: for example, if we want to print the memory hexadecimal representation of a short integer (short)-1, on the Win32 platform, a short type occupies 2 bytes, so we naturally want to use 4 to print it as hexadecimal digits: 
short si = -1; 
sprintf(s, "%04X", si); 
yields "FFFFFFFF", what's going on? Because spritnf is a variable parameter function, except for the first two parameters, the latter parameters are not type-safe, and there is no way for the function to know that the parameters were pushed on the stack before the function call just through a "%X". Is it a 4-byte integer or a 2-byte short integer, so a unified 4-byte processing method is adopted, which results in sign extension when the parameter is pushed onto the stack, and expanded to a 32-bit integer -1. 4 positions are not enough, so the 8-bit hexadecimal of the 32-bit integer -1 is printed out.

If you want to see si as it is, then you should ask the compiler to do 0-extend instead of sign-extend (the left side of the extension is 0 instead of the sign bit): 
sprintf(s, "%04X", (unsigned short)si ); 
just fine. Or: 
unsigned short si = -1; 
sprintf(s, "%04X", si);

sprintf and printf can also print integer strings in octal, using "%o". Note that neither octal nor hexadecimal will 
print negative numbers, they are all unsigned, which is actually the direct hexadecimal or octal representation of the internal encoding of the variable.

2. Control the printing format of floating-point numbers


The printing and format control of floating-point numbers is another common function of sprintf. Floating-point numbers are controlled by the format character "%f", and 6 digits after the decimal point are 
reserved  , for example:
sprintf(s, "%f", 3.1415926); //Generate "3.141593" 
but sometimes we want to control the width and decimal places of printing, then we should use: "%m.nf" format, where m 
represents the width of printing, and n represents the number of digits after the decimal point. For example: 
sprintf(s, "%10.3f", 3.1415626); //Generates: " 3.142" 
sprintf(s, "%-10.3f", 3.1415626); //Generates: "3.142" 
sprintf(s, "%. 3f", 3.1415626); //Do not specify the total width, yields: "3.142"

Note a question, what do you think 
int i = 100; 
sprintf(s, "%.2f", i); 
will print? "100.00"? Is it right? Just try it yourself, and also try the following: 
sprintf(s, "%.2f", (double)i); 
The first one is definitely not the correct result, the reason is the same as mentioned above, the parameter The caller did not know that the format controller corresponding to i was a "%f" when the stack was pushed. When the function is executed, the function itself does not know that what was pushed into the stack was an integer that year, so the poor 4 bytes that hold the integer i are forced to be interpreted as a floating-point number format without any explanation, and the whole mess is messed up. However, if anyone is interested in hand-coding a floating-point number, you can use this method to check that your hand-coded result is correct.

3. Characters

/Ascii

code comparison

We know that in the C/C++ language, char is also a common scalable type. Except for the word length, it has no essential difference from short, int, and long. It is just used by everyone to represent characters and strings. That's it. (Maybe this type should be called "byte" back then, and now you can use byte or short to define char through typedef according to the actual situation, which is more appropriate) So, use "%d" or "%x" to print A character, you can get its decimal or hexadecimal ASCII code; conversely, use "%c" to print an integer, you can see its corresponding ASCII character. The following program segment prints the ASCII code comparison table of all visible characters to the screen (printf is used here, note that when "#" and "%X" are used together, "0X" prefix is ​​automatically added to the hexadecimal number): 
for(int i = 32; i < 127; i++) { 
printf("[ %c ]: %3d 0x%#04X\n", i, i, i); 
}

Fourth, the connection string


Since you can insert various things into the format control string of sprintf and finally "connect them into a string", you can naturally concatenate strings, which can replace strcat in many cases, but sprintf can concatenate multiple strings at a time (naturally). You can also insert other content between them at the same time, which is very flexible anyway). For example: 
char* who = "I"; 
char* whom = "CSDN"; 
sprintf(s, "%s love %s.", who, whom); //Generate: "I love CSDN. " 
strcat can only connect String (a ''-terminated character array or character buffer, null-terminated-string), but sometimes we have two character buffers, they are not '' terminated. For example, many character arrays returned from third-party library functions, and character streams read in from hardware or network transmission, they may not end with a corresponding '' after each character sequence. If direct connection, whether it is sprintf or strcat will definitely lead to illegal memory operation, and strncat also requires at least the first parameter to be a null-terminated-string, what should we do? We will naturally recall that the width can be specified when printing integers and floating-point numbers, and the same is true for strings. For example: 
char a1[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'}; 
char a2[] = {'H', 'I' , 'J', 'K', 'L', 'M', 'N'}; 
If: 
sprintf(s, "%s%s", a1, a2); //Don't do that! 
Nine times out of ten, something will go wrong. Can it be changed to: 
sprintf(s, "%7s%7s", a1, a2); 
Not much better, the correct one should be: 
sprintf(s, "%.7s%.7s", a1, a2);//Generate: "ABCDEFGHIJKLMN" 
This can be analogous to "%m.nf" that prints floating point numbers , in "%m.ns", m represents the occupied width (fill a space if the string length is insufficient, and print it according to the actual width), and n represents the maximum number of characters taken from the corresponding string. Usually m is not very useful when printing strings, or the n after the dot is used more. Naturally, you can also take only some characters before and after: 
sprintf(s, "%.6s%.5s", a1, a2);//Generate: "ABCDEFHIJKL" 
In many cases, we may also want to use these format control characters with The number that specifies the length information is dynamic, not statically specified, because in many cases, the program will not know how many characters in the character array need to be taken at run time. This dynamic width/precision setting function is used in sprintf is also taken into account in the implementation of sprintf using "*" to occupy a position that would otherwise require a constant number with a specified width or precision, and again, the actual width or precision can be provided like any other variable being printed, Thus, the above example can become: 
sprintf(s, "%.*s%.*s", 7, a1, 7, a2); 
or: 
sprintf(s, "%.*s%.*s", sizeof(a1), a1, sizeof(a2), a2); 
In fact, the previously introduced printing characters, integers, floating-point numbers, etc. can dynamically specify those constant values, such as: 
sprintf(s, "%-*d", 4, 'A'); // yields "65" 

sprintf(s, "%*.*f", 10, 2, 3.1415926); //产生" 3.14"

5. Print address information


Sometimes when debugging a program, we may want to view the addresses of some variables or members. Since addresses or pointers are just 32-bit numbers, you can print them out using "%u" which prints unsigned integers: 
sprintf(s , "%u", &i); 
but usually people prefer to display an address in hexadecimal rather than decimal: 
sprintf(s, "%08X", &i); 
However, these are indirect methods, for Address printing, sprintf provides a special "%p": 
sprintf(s, "%p", &i); 
I think it is actually equivalent to: 
sprintf(s, "%0*x", 2 * sizeof(void *), &i); 
Using the return value of sprintf 
Less attention is paid to the return value of the printf/sprintf function, but it is sometimes useful. spritnf returns the number of characters that were eventually printed into the character buffer by this function call. That is to say, every time after a sprinf call ends, you don't need to call strlen again to know the length of the result string. For example: int len ​​= sprintf(s, "%d", i); For positive integers, len is equal to the decimal digits of integer i. The following is a complete example that generates 10 random numbers between [0, 100) and prints them into a character array s, separated by commas. 
#include 
#include 
#include 
int main() { 
srand(time(0)); 


for(int i = 0; i < 10; i++) { 
offset += sprintf(s + offset, "%d,", rand() % 100); 

s[offset - 1] = '\n';/ / Replace the last comma with a newline. 
printf(s); 
return 0; 

Imagine when you fetch a record from the database, and then want to concatenate their fields into a string according to certain rules, you can use this method. In theory, he It should be more efficient than constant strcat, because strcat needs to find the last '' position for each call, and in the example given above, we use the sprintf return value to directly record this position every time.

6. Use

sprintf

FAQ


sprintf is a variadic function, and there are often problems when using it, and as long as there is a problem, it is usually a memory access error that can cause the program to crash. Fortunately, although the problem caused by the misuse of sprintf is serious, it is easy to find. It is nothing more than a few In this case, you can usually see the wrong code with your eyes and look at it a few more times.


  1. The length of the first parameter of the buffer overflow  is too short, no need to say, give it a bigger place. Of course, it may also be a problem with the following parameters. It is recommended that you must be careful about changing parameters. When printing strings, try to specify the maximum number of characters in the form of "%.ns".
  2. I forgot that the first parameter is too low-level to be a low-level problem, and I am too used to using printf. // Occasionally commits frequently.
  3. The problem of variable parameter correspondence 
    is usually that you forget to provide the variable parameter corresponding to a certain format character, which leads to all the misplaced parameters in the future. Check it. Especially those parameters corresponding to "*", are all provided? Don't map an integer to a "%s", the compiler will think you're cheating her too much (the compiler is the mother of obj and exe, she should be a woman, :P).

7. Other


strftime 
sprnitf also has a good cousin: strftime, which is specially used for formatting time strings. The maximum length of the buffer may be to pass the buck in the event of a problem. Here is an example: 
time_t t = time(0); 
//Generate a string in "YYYY-MM-DD hh:mm:ss" format. 
char s[32]; 
strftime(s, sizeof(s), "%Y-%m-%d %H:%M:%S", localtime(&t)); 
sprintf can also find his bosom friend in MFC : CString::Format, strftime naturally has her counterpart in MFC: 
CTime::Format, this pair is sponsored by object-oriented, and the code used to write is more elegant

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325018223&siteId=291194637