关于
linux命令或者接口中会存在很多format string的场景,本文将整理使用到的各种场景和解释。
printf
Format String: %[flags][minimum_field_width][.precision][length_modifier]conversion_specifier
不想写解释了, 直接看man 3 printf, 里面描述的很清晰
# # 标识添加各进制的前缀, 比如十六进制的0x等
# 0 padding zero
# - left justiment, 默认右对齐
# + 显式显示正负号
# ' 显示thousands separator, BASH的printf支持, GCC版本貌似不支持
flags: [+-0#']
# Minimum Field Width
标识整个字段的最小宽度, 如果超过, 不会截断
# Precision
对于d, i, o, u, x, X的conversion specifier, 表示最小出现的数字个数; 而对于a, A, f, F, e, E, 表示小数点后数字个数; 对于g, G, 表示最大有效位数; 对于s, S, 表示最大现实字符个数
# minimum_field_width和precision可以使用后面的argument参数作为值, 使用 '*' 标识, 比如
int width = 3;
int num = 2;
printf("%*d\n", width, num);
printf("%2$%*1$d\n");
# 打印出 __2
# 以上两种方式一样, 第一种使用*占位一个参数作为minimum_field_width, 第二种更加隐晦, 她使用 '$m$' 格式引用后面的参数, 然后使用这种形式引用第一个参数作为minimum_field_width。
# Length Modifier
A length modifier is used to exactly specify the type of the matching argument.
在printf中一般不使用, 标识被匹配参数的类型或者叫做长度, 比如
printf("%hhd\n", 257)
打印 1, 因为 'hh' 表示后面的参数是一个字节, 最大0xff, 超过就wrap
# Conversion Specifier
d, i, u, x, X, o, O, f, F, e, E, g, G, c, s, % etc
# 当conversion specifier为x时, 有pricision场景, flag 0省略, 所以前面5位空格
printf "%08.3x" 7 -> _____007
printf "%-08.3x" 7 -> 007_____
flags: 0 表示不足长度8使用0填充
width: 8 表示最长长度为8
precision: 3 使用x时, precision表示最短长度
specifier: x 表示使用16进制表示
sscanf
sscanf的format格式介绍
%[*][maximum_field_width][length_modifier]conversion_specifier
| format string | Description | Example | Matching Input | Results |
|---|---|---|---|---|
| * | 匹配输入但不赋值 | int anInput; scanf("%*s %i", &anInput); | Age: _29 | anInput = 29, return value = 1 |
| maximum_field_width | 标识匹配输入的字节个数 | int anInt; char s[10]; scanf("%2i", &anInt); scanf("%9s", s); | 2345 VeryLongString | anInt==23, return value==1 s=="VeryLongS" return value==1 |
| length_modifier | 标识目的匹配参数的类型(或者叫做大小) | char *src="12"; char des[2]; sscanf(src, "%2hhx\n", des); printf("des[0] = %d\n",des[0]); | des[0] = 12 |
sscanf返回值
正常情况成功返回成功匹配和赋值的个数。 如果部分成功匹配和赋值, 返回成功的个数; 如果input结束(EOF)时, 没有匹配成功或者匹配失败, 返回EOF, 使用errno查看。
// https://wpollock.com/CPlus/PrintfRef.htm#printfLen
char buf[BUFSIZ], junk[BUFSIZ];
int income;
fprintf( stderr, "Please enter your income: " );
// Loop until the user enters a correct value:
while ( fgets( buf, sizeof(buf), stdin ) != NULL )
{
if ( sscanf( buf, "%i%[^\n]", &income, junk ) == 1 )
break;
// Do some sort of error processing:
fprintf( stderr, "\nError reading your income, please try again.\n" );
fprintf( stderr, "Please enter your income: " );
}
// income correctly enters read at this point.
hexstr2buf
unsigned char *hexstr2buf(const char *str)
{
size_t len = strlen(str);
if (len % 2 != 0)
return NULL;
size_t res_len = len / 2 + 1;
unsigned char *res = (unsigned char *)malloc(res_len);
for (int i = 0; i < len; i += 2)
{
sscanf(str + i, "%2hhx", res + i / 2);
}
*(res + res_len - 1) = '\0';
return res;
}
hexdump
- KB and K(KiB)
hexdump可以使用K或者KiB代表1024字节, KB代表1000字节 - Format and Color in HEXDUMP
format string格式:
-e 'iterator_count/byte_count "format"', 其中iterator_count, byte_count其中只要有一个存在, 那/是必须的。format必须使用双引号,format以%开头, 类似printf。常见的format有:
_a每次开始递归迭代时执行, 比如-e '"%08.8_ax"'
_A所有迭代完成后执行, 一般最后输出所有长度, 比如:-e '"%08.8_Ax"'
_p按照当前字符集输出字符, 不能打印使用.代替
_L添加颜色
x16进制转化
# 执行完所有转换添加cyan颜色的地址标识, 然后换行;
# 每次开始递归迭代时添加cyan颜色的起始地址标识, 空格两个
# 每次迭代八次, 每次取两个字节, 如果开头两个字节是0x6f72则使用绿色显示, 否则使用红色, 其他使用默认颜色
# 每次迭代十六次, 使用默认字符集显示对应字节的字符, 并且前后使用 "|" 分割, 最后换行
hexdump -v -e '"%08_Ax_L[cyan]\n"' -e '"%08_ax_L[cyan] " 8/2 "%04x_L[green:0x6f72@0-1,!red:0x6f72@0-1] " " |"' -e '16/1 "%_p" "|" "\n"' -n 64 /etc/passwd
# -v 不省略重复字节, 默认会使用 * 省略重复字节
# -e 提供format, 可以有多个, 多个的递归迭代从当前开头开始
# -f 提供format文件
# -n 使用前64个字节
- xxd和hexdump分场景使用, hexdump支持定制, 功能更丰富, 但是简单场景xxd似乎更适合点 :)
echo -en "tls13 $label" | hexdump -v -e '/1 "%02x"'
echo -en "tls13 $label" | xxd -p