C語言實現類似C#的格式化輸出

在C#中,格式化輸出可以使用索引占位符以及復合格式的占位符,可以不用關心后面參數是什么類型,使用起來非常方便,如下簡單的示例:

Console.WriteLine("{2} {1} {0} {{{2}}}", "Hello, World!", 1, 8.8);

會輸出:

8.8 1 Hello, World! {8.8}

規范可以參見:復合格式設置

在C語言標準庫中使用的printf系列函數中,需要使用格式化字符串明確指定后面參數的類型,如果指定錯誤,可能會引發災難! 那C語言,可以實現上述功能嗎 ? 在C11泛型的加持下,使用關鍵字_Generic也可以實現上述功能了,下面筆者就一步步來實現,并且支持可以不指定索引,支持C標準格式化字符串。

一、實現效果

我們假定要實現的是printxprint,其使用的方式如下:

main.c:

#include "printx.h"int main() {char a = 'A';unsigned char b = 130;int age = 25;unsigned int ui = 123456;long l = 12345678;unsigned ul = 123456789;long long ll = 98761234567890;unsigned long long ull = 9998761234567890;float f = 2.71828F;double pi = 3.14159;const char* name = "Witton";unsigned int hexv = 8192;auto x = '\n';printx("test\n");printx("Name: {{{}}}, Age: {}\n", name, age);printx("Age: {1}, Name: {}\n", name, age);printx("Name: {0}, Age: {1}\n", name, age);printx("Pi: {0:.2f}\n", pi);printx("Hex: {0:#x}, Again: {0:#08x}\n", hexv);printx("Swap order: {1}, {0}\n", "first", "second");printx("Char: {0}\n", (char)'A');printx("Repeat: {0} {0} {1:.1f} {}\n", age, pi, f);printx("1:{} 2:{} 3:{} 4:{} 5:{}, 6:{}, 7:{}, 8:{}, 9:{}, 10:{}\n", name, a,b, age, ui, l, ul, ll, ull, f);print(name, a, b, age, ui, l, ul, ll, ull, (char)'\n');return 0;
}

其運行結果為:

test
Name: {Witton}, Age: 25
Age: 25, Name: Witton  
Name: Witton, Age: 25  
Pi: 3.14
Hex: 0x2000, Again: 0x002000
Swap order: second, first
Char: A
Repeat: 25 25 3.1 25
1:Witton 2:A 3:130 4:25 5:123456, 6:12345678, 7:123456789, 8:98761234567890, 9:9998761234567890, 10:2.718280
Witton A 130 25 123456 12345678 123456789 98761234567890 9998761234567890

在這里插入圖片描述

需要注意的是:C語言中字面字符的類型是int,而不是char,所以:

printx("Char: {0}\n", (char)'A');

中需要強制轉換成char類型才能正常輸出字母A。

在這里插入圖片描述

二、統一類型

由于在調用的過程中傳入的參數可能是各種數據類型,我們需要統一轉換成一種自定義類型,在自定義類型中去標識實際的數據類型。

typedef enum {T_UNKNOWN,T_CHAR,T_BYTE,T_STRING,T_BYTES,T_INT,T_UINT,T_LONG,T_ULONG,T_LONGLONG,T_ULONGLONG,T_FLOAT,T_DOUBLE,
} EDataType;typedef struct {EDataType type;union {char c;unsigned char b;int i;unsigned int u;long l;unsigned long ul;long long ll;unsigned long long ull;float f;double d;const char* s;const unsigned char* bs;} v;
} FmtArg;static inline FmtArg make_char(char v) {return (FmtArg){T_CHAR, {.c = v}};
}static inline FmtArg make_uchar(unsigned char v) {return (FmtArg){T_BYTE, {.b = v}};
}static inline FmtArg make_string(const char* s) {return (FmtArg){T_STRING, {.s = s}};
}static inline FmtArg make_bytes(const unsigned char* s) {return (FmtArg){T_BYTES, {.bs = s}};
}static inline FmtArg make_int(int v) {return (FmtArg){T_INT, {.i = v}};
}
static inline FmtArg make_uint(unsigned int v) {return (FmtArg){T_UINT, {.u = v}};
}static inline FmtArg make_long(long v) {return (FmtArg){T_LONG, {.l = v}};
}static inline FmtArg make_ulong(unsigned long v) {return (FmtArg){T_ULONG, {.ul = v}};
}static inline FmtArg make_longlong(long long v) {return (FmtArg){T_LONGLONG, {.ll = v}};
}static inline FmtArg make_ulonglong(unsigned long long v) {return (FmtArg){T_ULONGLONG, {.ull = v}};
}static inline FmtArg make_float(float v) {return (FmtArg){T_FLOAT, {.f = v}};
}static inline FmtArg make_double(double v) {return (FmtArg){T_DOUBLE, {.d = v}};
}static inline FmtArg make_unknown(void) {return (FmtArg){T_UNKNOWN};
}

有了自定義類型了,就可以聲明具體實現的C函數了:

void printx_impl(const char* fmt, int arg_count, FmtArg* argv);

前面的fmt就是類似C#的格式符,也可以是nullptr,表明不需要格式字符串,自動依次使用參數;arg_count表明有幾個參數,決定著后面參數argv的個數;argv為實際的參數信息。

而給用戶調用的API,printxprint其實是一個宏,它類似如下聲明:

#define printx(fmt, ...)
#define print(...)

我們需要在宏中調用實際工作的C函數printx_impl,在調用前需要將傳入的參數轉換成自定義數據類型FmtArg,并且準備好printx_impl函數需要的參數。

三、計算參數個數

如何計算宏參數...中包含的參數個數?
在C/C++中參數... 可能包含0個到多個參數,我們假定最多支持10個參數。為了計算宏參數個數,需要定義一個匹配或者說是取宏參數的宏:

#define GET_MACRO(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, n, ...) n

GET_MACRO的參數列表是:_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, n, ...,這里的...表示剩余參數,但在宏定義中,我們只取n

然后定義計算宏參數個數的宏:

#define COUNT_ARGS(...) \GET_MACRO(0, ##__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

__VA_ARGS__中可能是0個參數,C/C++編譯器有一種寫法:##__VA_ARGS__,當是0個時,會把前面的逗號去掉,變為:

GET_MACRO(0, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

GET_MACRO第12個參數,結果為0

如果有多個參數,則依次填充。比如,參數1,2,3,變為:

GET_MACRO(0, 1, 2, 3, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

GET_MACRO第12個參數,結果為3

四、轉換參數

通過前面的方法,我們知道了參數個數,接下來就是實現參數類型的轉換了,要實現類型轉換就需要知道是什么類型,C11提供了_Generic關鍵字,只有它才能識別類型,實現泛型。其語法參見:https://cppreference.cn/w/c/language/generic

定義如下宏來將參數轉換成自定義類型FmtArg

#define MAKE_FMTARG(x)                    \_Generic((x),                           \char: make_char,                    \unsigned char: make_uchar,          \const char*: make_string,           \char*: make_string,                 \const unsigned char*: make_bytes,   \unsigned char*: make_bytes,         \int: make_int,                      \unsigned int: make_uint,            \long: make_long,                    \unsigned long: make_ulong,          \long long: make_longlong,           \unsigned long long: make_ulonglong, \float: make_float,                  \double: make_double,                \default: make_unknown)(x)

使用獲取參數個數一樣的方法,我們也可以逐個取參數,然后調用MAKE_FMTARG進行轉換,先定義一組宏:

#define APPLY0(m, a) 0 // 這里定義為0,避免編譯器警告
#define APPLY1(m, a) m(a)
#define APPLY2(m, a, ...) m(a), APPLY1(m, __VA_ARGS__)
#define APPLY3(m, a, ...) m(a), APPLY2(m, __VA_ARGS__)
#define APPLY4(m, a, ...) m(a), APPLY3(m, __VA_ARGS__)
#define APPLY5(m, a, ...) m(a), APPLY4(m, __VA_ARGS__)
#define APPLY6(m, a, ...) m(a), APPLY5(m, __VA_ARGS__)
#define APPLY7(m, a, ...) m(a), APPLY6(m, __VA_ARGS__)
#define APPLY8(m, a, ...) m(a), APPLY7(m, __VA_ARGS__)
#define APPLY9(m, a, ...) m(a), APPLY8(m, __VA_ARGS__)
#define APPLY10(m, a, ...) m(a), APPLY9(m, __VA_ARGS__)#define APPLY(m, ...)                                                          \GET_MACRO(0, ##__VA_ARGS__, APPLY10, APPLY9, APPLY8, APPLY7, APPLY6, APPLY5, \APPLY4, APPLY3, APPLY2, APPLY1, APPLY0)(m, __VA_ARGS__)

這組宏支持0~10個參數,對每個參數調用mm可以是宏,也可以是函數。我們這里需要調用的是MAKE_FMTARG宏。

五、實現printxprint

現在可以寫出printxprint宏的實現了:

#define printx(fmt, ...)                            \printx_impl(fmt, COUNT_ARGS(__VA_ARGS__), \(FmtArg[]){APPLY(MAKE_FMTARG, ##__VA_ARGS__)})#define print(...)                                \printx_impl(0, COUNT_ARGS(__VA_ARGS__), \(FmtArg[]){APPLY(MAKE_FMTARG, ##__VA_ARGS__)})

(FmtArg[]){APPLY(MAKE_FMTARG, ##__VA_ARGS__)}構建了一個FmtArg的數組,如果參數個數為0,則是(FmtArg[]){0}

六、擴展實現fprintxfprint

目前的API還只支持輸出到標準輸出設備stdout,無法輸出到文件,比如想輸出到日志文件,只需要添加一個FILE* fp參數即可,相應修改如下:

void printx_impl(FILE* fp, const char* fmt, int arg_count, FmtArg* argv);#define printx(fmt, ...)                            \printx_impl(stdout, fmt, COUNT_ARGS(__VA_ARGS__), \(FmtArg[]){APPLY(MAKE_FMTARG, ##__VA_ARGS__)})#define print(...)                                \printx_impl(stdout, 0, COUNT_ARGS(__VA_ARGS__), \(FmtArg[]){APPLY(MAKE_FMTARG, ##__VA_ARGS__)})

實現fprintxfprint宏:

#define fprintx(fp, fmt, ...)                   \printx_impl(fp, fmt, COUNT_ARGS(__VA_ARGS__), \(FmtArg[]){APPLY(MAKE_FMTARG, ##__VA_ARGS__)})#define fprint(fp, ...)                       \printx_impl(fp, 0, COUNT_ARGS(__VA_ARGS__), \(FmtArg[]){APPLY(MAKE_FMTARG, ##__VA_ARGS__)})

七、源碼

printx.h:

// copyright(C), author: Witton
// email: witton@163.com#ifndef _PRINT_X_H_INCLUDE_
#define _PRINT_X_H_INCLUDE_#include <stdio.h>typedef enum {T_UNKNOWN,T_CHAR,T_BYTE,T_STRING,T_BYTES,T_INT,T_UINT,T_LONG,T_ULONG,T_LONGLONG,T_ULONGLONG,T_FLOAT,T_DOUBLE,
} EDataType;typedef struct {EDataType type;union {char c;unsigned char b;int i;unsigned int u;long l;unsigned long ul;long long ll;unsigned long long ull;float f;double d;const char* s;const unsigned char* bs;} v;
} FmtArg;static inline FmtArg make_char(char v) {return (FmtArg){T_CHAR, {.c = v}};
}static inline FmtArg make_byte(unsigned char v) {return (FmtArg){T_BYTE, {.b = v}};
}static inline FmtArg make_string(const char* s) {return (FmtArg){T_STRING, {.s = s}};
}static inline FmtArg make_bytes(const unsigned char* s) {return (FmtArg){T_BYTES, {.bs = s}};
}static inline FmtArg make_int(int v) {return (FmtArg){T_INT, {.i = v}};
}
static inline FmtArg make_uint(unsigned int v) {return (FmtArg){T_UINT, {.u = v}};
}static inline FmtArg make_long(long v) {return (FmtArg){T_LONG, {.l = v}};
}static inline FmtArg make_ulong(unsigned long v) {return (FmtArg){T_ULONG, {.ul = v}};
}static inline FmtArg make_longlong(long long v) {return (FmtArg){T_LONGLONG, {.ll = v}};
}static inline FmtArg make_ulonglong(unsigned long long v) {return (FmtArg){T_ULONGLONG, {.ull = v}};
}static inline FmtArg make_float(float v) {return (FmtArg){T_FLOAT, {.f = v}};
}static inline FmtArg make_double(double v) {return (FmtArg){T_DOUBLE, {.d = v}};
}static inline FmtArg make_unknown(void) {return (FmtArg){T_UNKNOWN};
}#define MAKE_FMTARG(x)                    \_Generic((x),                           \char: make_char,                    \unsigned char: make_byte,          \const char*: make_string,           \char*: make_string,                 \const unsigned char*: make_bytes,   \unsigned char*: make_bytes,         \int: make_int,                      \unsigned int: make_uint,            \long: make_long,                    \unsigned long: make_ulong,          \long long: make_longlong,           \unsigned long long: make_ulonglong, \float: make_float,                  \double: make_double,                \default: make_unknown)(x)#define GET_MACRO(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, n, ...) n#define COUNT_ARGS(...) \GET_MACRO(0, ##__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)#define APPLY0(m, a) 0  // 這里定義為0,避免編譯器警告
#define APPLY1(m, a) m(a)
#define APPLY2(m, a, ...) m(a), APPLY1(m, __VA_ARGS__)
#define APPLY3(m, a, ...) m(a), APPLY2(m, __VA_ARGS__)
#define APPLY4(m, a, ...) m(a), APPLY3(m, __VA_ARGS__)
#define APPLY5(m, a, ...) m(a), APPLY4(m, __VA_ARGS__)
#define APPLY6(m, a, ...) m(a), APPLY5(m, __VA_ARGS__)
#define APPLY7(m, a, ...) m(a), APPLY6(m, __VA_ARGS__)
#define APPLY8(m, a, ...) m(a), APPLY7(m, __VA_ARGS__)
#define APPLY9(m, a, ...) m(a), APPLY8(m, __VA_ARGS__)
#define APPLY10(m, a, ...) m(a), APPLY9(m, __VA_ARGS__)#define APPLY(m, ...)                                                          \GET_MACRO(0, ##__VA_ARGS__, APPLY10, APPLY9, APPLY8, APPLY7, APPLY6, APPLY5, \APPLY4, APPLY3, APPLY2, APPLY1, APPLY0)(m, __VA_ARGS__)#define printx(fmt, ...)                            \printx_impl(stdout, fmt, COUNT_ARGS(__VA_ARGS__), \(FmtArg[]){APPLY(MAKE_FMTARG, ##__VA_ARGS__)})#define print(...)                                \printx_impl(stdout, 0, COUNT_ARGS(__VA_ARGS__), \(FmtArg[]){APPLY(MAKE_FMTARG, ##__VA_ARGS__)})#define fprintx(fp, fmt, ...)                   \printx_impl(fp, fmt, COUNT_ARGS(__VA_ARGS__), \(FmtArg[]){APPLY(MAKE_FMTARG, ##__VA_ARGS__)})#define fprint(fp, ...)                       \printx_impl(fp, 0, COUNT_ARGS(__VA_ARGS__), \(FmtArg[]){APPLY(MAKE_FMTARG, ##__VA_ARGS__)})void printx_impl(FILE* fp, const char* fmt, int arg_count, FmtArg* argv);#endif

printx.c:

// copyright(C), author: Witton
// email: witton@163.com#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include "printx.h"#if __STDC_VERSION__ >= 202311L
#ifdef NULL
#undef NULL
#define NULL nullptr
#endif
#endifstatic void print_one_arg(FILE* fp, const char* fmt_spec, FmtArg arg) {if (!fmt_spec[0]) {switch (arg.type) {case T_CHAR:(void)fprintf(fp,"%c", arg.v.c);break;case T_BYTE:(void)fprintf(fp,"%hhu", arg.v.b);break;case T_INT:(void)fprintf(fp,"%d", arg.v.i);break;case T_UINT:(void)fprintf(fp,"%u", arg.v.u);break;case T_LONG:(void)fprintf(fp,"%ld", arg.v.l);break;case T_LONGLONG:(void)fprintf(fp,"%lld", arg.v.ll);break;case T_ULONGLONG:(void)fprintf(fp,"%llu", arg.v.ull);break;case T_FLOAT:(void)fprintf(fp,"%f", arg.v.f);break;case T_DOUBLE:(void)fprintf(fp,"%lf", arg.v.d);break;case T_STRING:(void)fprintf(fp,"%s", arg.v.s);break;default:(void)fprintf(fp,"<?(by witton)>");break;}return;}char real_fmt[64];(void)snprintf(real_fmt, sizeof(real_fmt), "%%%s", fmt_spec);switch (arg.type) {case T_CHAR:(void)fprintf(fp,real_fmt, arg.v.c);break;case T_BYTE:(void)fprintf(fp,real_fmt, arg.v.b);break;case T_INT:(void)fprintf(fp,real_fmt, arg.v.i);break;case T_UINT:(void)fprintf(fp,real_fmt, arg.v.u);break;case T_LONG:(void)fprintf(fp,real_fmt, arg.v.l);break;case T_LONGLONG:(void)fprintf(fp,real_fmt, arg.v.ll);break;case T_ULONGLONG:(void)fprintf(fp,real_fmt, arg.v.ull);break;case T_FLOAT:(void)fprintf(fp,real_fmt, arg.v.f);break;case T_DOUBLE:(void)fprintf(fp,real_fmt, arg.v.d);break;case T_STRING:(void)fprintf(fp,real_fmt, arg.v.s);break;default:(void)fprintf(fp,"<?(by witton)>");break;}
}static inline void handle_printx_fmt(FILE* fp, const char* p,const char* end,int arg_count,FmtArg* argv) {char index_str[16] = {0};char spec[32] = {0};int idx = 0;const char* colon = memchr(p + 1, ':', end - (p + 1));if (colon) {size_t len_idx = colon - (p + 1);strncpy(index_str, p + 1, len_idx);strncpy(spec, colon + 1, end - (colon + 1));} else {strncpy(index_str, p + 1, end - (p + 1));}if (isdigit((unsigned char)index_str[0])) {idx = strtol(index_str, NULL, 10);if (idx >= 0 && idx < arg_count) {print_one_arg(fp, spec, argv[idx]);} else {(void)fprintf(fp, "<BAD_INDEX(by witton)>");}} else {(void)fprintf(fp, "<BAD_FORMAT(by witton)>");}
}void printx_impl(FILE* fp, const char* fmt, int arg_count, FmtArg* argv) {if (NULL == fmt) {for (int i = 0; i < arg_count; ++i) {print_one_arg(fp, "", argv[i]);(void)putc(' ', fp);}return;}int used_args = 0;const char* p = fmt;while (*p) {if (p[0] == '{' && p[1] == '}' && used_args < arg_count) {print_one_arg(fp, "", argv[used_args++]);p += 2;  // 跳過 '}'} else if (*p == '{' && *(p + 1) != '{') {const char* end = strchr(p, '}');if (!end) {(void)putc(*p++, fp);continue;}handle_printx_fmt(fp, p, end, arg_count, argv);p = end + 1;} else if (*p == '{' && *(p + 1) == '{') {(void)putc('{', fp);p += 2;} else if (*p == '}' && *(p + 1) == '}') {(void)putc('}', fp);p += 2;} else {(void)putc(*p++, fp);}}
}

至此,我們可以像C#一樣寫格式化輸出代碼了,可以不擔心格式符寫錯了。但是如果了使用自定義格式符,即類似{1:.1f}中有冒號后面標準C格式符,則依舊需要小心格式符是否寫正確!

如果本文對你有幫助,歡迎點贊收藏!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/918887.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/918887.shtml
英文地址,請注明出處:http://en.pswp.cn/news/918887.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

一人公司方法論

** 一人公司方法論 ** 那什么是一人公司&#xff1f; 字面的理解就是一個人運營的公司&#xff0c;但實際上它指代的是比較少的人運營的小公司&#xff0c;一般來說 1 ~ 3 個人運營的公司&#xff0c;也可以把它放到一人公司的范圍以內。其他一些形式&#xff0c;比如說一個人再…

Ceph CSI 鏡像刪除流程與 Trash 機制失效問題分析文檔

#作者&#xff1a;閆乾苓 文章目錄一、問題背景二、實際行為三、源碼分析四、分析與推論五、期望行為與建議優化六、結論一、問題背景 在生產環境中&#xff0c;為避免因誤操作導致的永久數據丟失&#xff0c;Ceph RBD 提供了 Trash 功能&#xff0c;允許將鏡像“軟刪除”至回…

.NET Framework 3.5 不原生支持PreApplicationStartMethod特性

.NET Framework 3.5 不原生支持PreApplicationStartMethod特性。這個特性是在 .NET Framework 4.0 中引入的&#xff0c;用于在應用程序啟動早期執行初始化邏輯。 在.NET 3.5 中&#xff0c;如果你需要實現類似的 “應用啟動時自動注冊模塊” 功能&#xff0c;需要通過手動配置…

智能巡檢技術淺析

從機載智能硬件到深度學習算法&#xff0c;從實時邊緣計算到數字孿生平臺&#xff0c;無人機AI智能巡檢通過多模態感知、自主決策和持續進化&#xff0c;實現從"被動檢查"到"主動預防"的跨越式發展。機載智能硬件邊緣計算與機載AI芯片當代先進巡檢無人機已…

【圖像算法 - 11】基于深度學習 YOLO 與 ByteTrack 的目標檢測與多目標跟蹤系統(系統設計 + 算法實現 + 代碼詳解 + 擴展調優)

前言 詳細視頻介紹 【圖像算法 - 11】基于深度學習 YOLO 與 ByteTrack 的目標檢測與多目標跟蹤系統&#xff08;系統設計 算法實現 代碼詳解 擴展調優&#xff09;在計算機視覺應用中&#xff0c;目標檢測與多目標跟蹤的結合是實現智能視頻分析的關鍵。本文基于 YOLO 檢測模…

AI加持下的智能路由監控:Amazon VPC Direct Connect實戰指南

> 一次流量突增引發的生產事故,如何催生出融合流日志、機器學習與自動化告警的智能監控體系 深夜2點,電商平臺運維負責人李明的手機瘋狂報警——北美用戶下單量斷崖式下跌。他緊急登錄系統,發現跨境專線延遲飆升至2000ms。**經過3小時的排查**,罪魁禍首竟是新部署的CDN…

具身智能競速時刻,百度百舸提供全棧加速方案

2025年&#xff0c;全球具身智能賽道迎來快速發展期&#xff0c;技術方向日益清晰。每一家企業都面臨著同樣的核心命題&#xff1a;如何將前沿的模型能力&#xff0c;轉化為在真實世界各類場景中可規模化應用落地的機器人產品&#xff1f;這背后&#xff0c;是研發團隊對模型迭…

JavaScript 壓縮與混淆實戰:Terser 命令行詳解

使用 Terser 壓縮 JavaScript 文件&#xff08;基礎 現代語法問題解決&#xff09; 在前端開發中&#xff0c;隨著業務復雜度增加&#xff0c;JavaScript 文件體積越來越大。 文件大帶來的問題&#xff1a; 加載慢&#xff1a;文件越大&#xff0c;瀏覽器下載和解析時間越長…

【數據結構初階】--排序(三):冒泡排序、快速排序

&#x1f618;個人主頁&#xff1a;Cx330? &#x1f440;個人簡介&#xff1a;一個正在努力奮斗逆天改命的二本覺悟生 &#x1f4d6;個人專欄&#xff1a;《C語言》《LeetCode刷題集》《數據結構-初階》 前言&#xff1a;在上篇博客的學習中&#xff0c;我們掌握了直接選擇排序…

名詞概念:什么是尾部誤差?

“尾部誤差”就是指誤差分布在兩端的那一小撮、但數值特別大的誤差——也就是離中心&#xff08;均值/中位數&#xff09;很遠的“極端樣本”的誤差。對應統計學里的“分布尾部”&#xff08;tails&#xff09;。通俗點&#xff1a;大多數樣本誤差都很小&#xff0c;但總會有少…

記對外國某服務器的內網滲透

本專欄是筆者的網絡安全學習筆記&#xff0c;一面分享&#xff0c;同時作為筆記 文章目錄前文鏈接前言上線CS上線rdp后滲透信息收集SMB Pth攻擊權限維持魔幻上線提權關Windows Defenderend前文鏈接 WAMP/DVWA/sqli-labs 搭建burpsuite工具抓包及Intruder暴力破解的使用目錄掃描…

速賣通平臺關鍵字搜索商品列表列表接口實現指南:從接口分析到代碼落地

在跨境電商開發中&#xff0c;速賣通平臺的商品數據獲取是許多開發者關注的焦點。本文將詳細介紹如何實現速賣通關鍵字搜索商品列表接口&#xff0c;涵蓋接口請求參數分析、簽名機制、分頁處理及完整代碼實現&#xff0c;幫助開發者快速對接速賣通開放平臺。一、接口基本信息速…

UE UDP通信

1.確保工程為C工程&#xff0c;在項目工程的xx.Build.cs中加入Networking和Sockets模塊。PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "Networking", "Socke…

JavaScript 邏輯運算符與實戰案例:從原理到落地

JavaScript 中的邏輯運算符不僅是條件判斷的核心&#xff0c;還能通過“短路特性”簡化代碼&#xff1b;結合 DOM 操作的實戰案例&#xff0c;更能體現其靈活性。本文整理了邏輯運算符的個人理解、優先級規則&#xff0c;以及 4 個高頻實戰需求的實現方案&#xff0c;附個人思路…

Android RxJava 過濾與條件操作詳解

RxJava 是一個基于觀察者模式的響應式編程庫&#xff0c;在 Android 開發中被廣泛使用。其中&#xff0c;過濾和條件操作是 RxJava 中非常重要的一部分&#xff0c;它們允許我們對數據流進行精細控制。本文將詳細介紹 RxJava 中常用的過濾與條件操作符及其使用場景。一、過濾操…

云手機都具有哪些特點?

云手機擁有著便捷的遠程操作功能&#xff0c;讓用戶無論身處何地&#xff0c;只要能連接網絡&#xff0c;就能通過手機、電腦等終端設備遠程操控云手機&#xff0c;無需受限于物理位置&#xff0c;大大提升了工作的靈活性與便捷性。云手機主要是依賴于云計算技術&#xff0c;能…

Sparse-ICP—(4) 加權稀疏迭代最近點算法(matlab版)

目錄 一、算法原理 1、原理概述 2、參考文獻 二、代碼實現 三、結果展示 一、算法原理 1、原理概述 見:Sparse-ICP—(1)稀疏迭代最近點算法 2、參考文獻 二、代碼實現 SparseWeightedDistance.m function [move_points,T] =

統信UOS安裝NFS共享文件夾

在 UOS ARM 架構系統上安裝和配置 NFS 服務&#xff0c;實現與局域網中其他服務器共享文件夾的步驟如下&#xff1a;1. 安裝 NFS 服務首先更新系統并安裝 NFS 服務器組件&#xff1a;bash# 更新軟件包列表 sudo apt update# 安裝NFS服務器 sudo apt install nfs-kernel-server …

【完整源碼+數據集+部署教程】孔洞檢測系統源碼和數據集:改進yolo11-RetBlock

背景意義 研究背景與意義 隨著工業自動化和智能制造的快速發展&#xff0c;孔洞檢測作為關鍵的質量控制環節&#xff0c;受到了廣泛關注。孔洞的存在可能會影響產品的強度、密封性和整體性能&#xff0c;因此&#xff0c;準確、快速地檢測孔洞對于保障產品質量至關重要。傳統的…

k8s環境使用Operator部署Seaweedfs集群(一)

#作者&#xff1a;閆乾苓 文章目錄4.1 前置條件4.2 部署seaweedfs-operator4.3 準備operator鏡像SeaweedFS Operator是一個Kubernetes Operator&#xff0c;用于自動化部署和管理SeaweedFS集群 README.md:6-8 。部署分為兩個階段&#xff1a;首先部署Operator本身&#xff0c;然…