[操作系統] 基礎IO:系統文件I/O

在 Linux 操作系統中,文件 I/O(輸入/輸出)是程序與文件系統交互的基礎。理解文件 I/O 的工作原理對于編寫高效、可靠的程序至關重要。本文將深入探討系統文件 I/O 的機制。


一種傳遞標志位的方法

在 Linux 中,文件的打開操作通常使用標志位來指定文件的訪問模式。open() 系統調用用于打開文件,其原型如下:

int open(const char *pathname, int flags, mode_t mode);
  • pathname:要打開的文件路徑。
  • flags:打開文件時的標志位,指定文件的訪問模式和行為。
  • mode:文件權限,僅在創建新文件時使用。

常見的標志位包括:

參數必須包括以下三個訪問方式之一。

- `O_RDONLY`:只讀模式。
- `O_WRONLY`:只寫模式。
- `O_RDWR`:讀寫模式。

其他的訪問模式。

- `O_CREAT`:如果文件不存在,則創建文件。
- `O_TRUNC`:如果文件存在,則將其長度截斷為零。
- `O_APPEND`:每次寫入都追加到文件末尾。

標志位的原理:

原理就是位圖。不同的訪問模式位圖上的標記位置不同,傳參是通過或操作( | )即可得到需要訪問模式的位圖所有標記位置。然后再打開或操作文件時就會按照傳入的訪問模式進行。

文件權限mode

新創建文件的最終權限 = mode & ~umask

例如,以下代碼以讀寫模式打開文件 example.txt,如果文件不存在則創建:

int fd = open("example.txt", O_RDWR | O_CREAT, 0666);

在此,0666 是文件的權限掩碼,表示文件所有者、所屬組和其他用戶均具有讀寫權限。


hello.c 寫文件

在 C 語言中,使用 open() 打開文件后,可以使用 write() 系統調用向文件寫入數據。以下是一個示例:

#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main() {int fd = open("example.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);if (fd == -1) {// 錯誤處理return 1;}const char *text = "Hello, Linux!";ssize_t bytes_written = write(fd, text, strlen(text));if (bytes_written == -1) {// 錯誤處理close(fd);return 1;}close(fd);return 0;
}

fd中寫入buf,一次最多count個。

在此示例中:

  • open() 以寫入模式打開文件 example.txt,如果文件不存在則創建,權限為 0666
  • write() 將字符串 "Hello, Linux!" 寫入文件。
  • close() 關閉文件描述符,釋放資源。

每次寫入字符串不用留'\0'的位置,文件本身可以看做數組,如果中間存在'\0',則在讀取文件時會造成錯誤。

當向文件內寫入內容時,可以進行文本寫入和二進制寫入,兩者的區別寫入是語言層面的概念,系統不會關心類型,只要寫入內容就會直接寫入。

hello.c 讀文件

讀取文件的過程與寫入類似,使用 read() 系統調用從文件中讀取數據。示例如下:

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>int main() {int fd = open("example.txt", O_RDONLY);if (fd == -1) {// 錯誤處理return 1;}char buffer[128];ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);if (bytes_read == -1) {// 錯誤處理close(fd);return 1;}buffer[bytes_read] = '\0'; // 確保字符串以 null 結尾printf("File content: %s\n", buffer);close(fd);return 0;
}

fd中讀取,拷貝到buf中,最多讀取countbytes(sizeof(buf) - 1( - 1是為了在buf的末尾存貯'\0'))。

在此示例中:

  • open() 以只讀模式打開文件 example.txt
  • read() 從文件中讀取數據到緩沖區 buffer
  • close() 關閉文件描述符。

open 函數返回值

區分兩個概念:**系統調用****庫函數**

  • fopen``fclose``fread``fwrite等都是C標準庫中的函數,稱之為庫函數(libc)
  • open``close``read``write``lseek等屬于系統提供的接口,稱之為系統調用接口

通過上圖可以理解庫函數和系統調用之間的關系。可以認為f*系列的函數是對系統調用的封裝,方便二次開發。


open() 函數的返回值是一個文件描述符(fd),用于標識打開的文件。成功時返回非負整數,失敗時返回 -1,并設置 errno 以指示錯誤類型。常見的錯誤包括:

  • EACCES:權限不足。
  • ENOENT:文件不存在。
  • EINVAL:無效的標志位。

例如:

int fd = open("example.txt", O_RDONLY);
if (fd == -1) {perror("Error opening file");return 1;
}

文件描述符 fd

文件描述符(fd)是一個非負整數,用于標識進程打開的文件。標準輸入、標準輸出和標準錯誤分別對應文件描述符 0、1 和 2。文件描述符的分配規則如下:

  • 默認情況下,標準輸入、標準輸出和標準錯誤分別占用 0、1 和 2。
  • 通過 open() 打開的文件從 3 開始分配。

所以當我們查看在程序中打開的文件的fd時發現都是3之后的,就是因為在程序運行前就有自動升層的代碼在開頭打開了三個標準流文件,已經占據了0,1,2。

0 & 1 & 2

  • 0:標準輸入(stdin),通常對應鍵盤輸入。
  • 1:標準輸出(stdout),通常對應屏幕輸出。
  • 2:標準錯誤(stderr),用于輸出錯誤信息。

通過6可知

通過6中關于FILE的講解,當向open等函數返回值fd實際上就是進程內管理文件的數組的下標。所以當傳入close等函數調用時就會通過下標來找尋這個文件,然后進行文件操作。

而對于庫函數來說,返回值為FILE,作為將fd包裝好的結構體,在函數內部使用系統調用的時候會自行進行處理。

FILE

FILE是什么呢?

在 C 語言標準庫中,FILE 是一個用于描述文件的結構體,通常由 stdio.h 提供。它提供了一種便捷的接口,讓我們可以操作文件而無需直接涉及底層的文件描述符。

FILE 結構體的內部實現

FILE 結構體并不是操作系統原生的,而是由 C 標準庫(如 GNU C 庫)定義的,它封裝了文件的元數據,并提供了緩沖機制以提高 I/O 操作的效率。雖然不同的系統和編譯器可能有不同的實現,以下是 FILE 結構體的一種典型實現:

struct _iobuf {char *_ptr;       // 指向緩沖區的指針int _cnt;         // 緩沖區的剩余字節數char *_base;      // 緩沖區的起始位置int _flag;        // 文件狀態標志(如是否可讀、是否可寫)int _file;        // 文件描述符int _charbuf;     // 讀緩存區的狀態int _bufsiz;      // 緩沖區大小char *_tmpfname;  // 臨時文件名
};
typedef struct _iobuf FILE;

重要字段解釋:

  • _ptr:指向當前緩沖區位置的指針,文件數據會存儲在這里。
  • _cnt:緩沖區中剩余的可用空間字節數。
  • _base:緩沖區的起始位置。
  • _flag:存儲文件的狀態標志,如文件是否處于讀寫模式等。
  • _file:該文件對應的系統級文件描述符,這是最直接的文件標識。
  • _bufsiz:緩沖區的大小。
  • _tmpfname:如果文件是臨時的,存儲其文件名。

FILE 結構體內部使用緩沖機制,這使得每次文件 I/O 操作時,程序并不直接與磁盤交互,而是將數據存入內存中的緩沖區,等緩沖區滿時才將數據批量寫入磁盤,從而提高 I/O 性能。

緩沖機制具體本文不做解釋,之后文章會講解。


task_structfile_struct

Linux 中的進程是由 task_struct 結構體來描述的。每個進程的 task_struct 中都包含一個 *file指向一個file_struct,這個結構體管理著該進程打開的文件。

task_struct 和文件操作的聯系

task_struct 結構體代表一個進程。每個進程有自己的文件描述符表,文件描述符表由一個 file_struct 來表示。file_struct 存儲了進程打開的所有文件的描述符、文件指針等信息。

struct task_struct {...struct files_struct *files;  // 文件描述符表...
};
files_struct 結構體

files_struct 是與 task_struct 相關聯的結構體,存儲了該進程的文件描述符表(fd_table[])。它提供了一個對文件描述符的索引和文件操作的抽象管理。每個進程的 files_struct 都有一個 fd_table[] 數組,這個數組的索引即為文件描述符(fd)。

struct files_struct {atomic_t count;               // 引用計數,表示該文件描述符表被多少個進程共享struct fdtable *fdt;          // 文件描述符表(fd_table[])spinlock_t file_lock;         // 保護文件描述符表的鎖
};
fd_table[] 數組與 file_struct

fd_table[] 是一個數組,可以被看做文件描述符表,每個元素對應一個 file 結構體,表示一個文件。文件描述符(fd)就是 fd_table[] 數組的索引值。例如,文件描述符 0 對應標準輸入(stdin),文件描述符 1 對應標準輸出(stdout),文件描述符 2 對應標準錯誤(stderr)。

struct fdtable {unsigned int max_fds;         // 最大文件描述符數struct file **fd;             // 文件描述符數組,fd[i] 為進程打開的文件
};
  • fd[i] 表示索引為 i 的文件描述符指向的文件。
  • max_fds 表示文件描述符表的最大文件描述符數。
  • 不同的fd可以打開同一個文件,引用計數來維護,形成1 : n。
file 結構體

在 Linux 中,file 結構體表示一個打開的文件。它不僅包含了文件的數據指針和操作,還包含了與文件操作相關的狀態信息。file 結構體的關鍵部分包括:

struct file
{屬性mode讀寫位置讀寫選項緩沖區操作方法struct file *next; // 指向下一個fd的file結構體
}
  • f_op:文件操作結構體,包含了對文件的操作方法(如讀取、寫入、關閉等)。
  • f_pos:文件的當前偏移量,表示文件指針的位置。
  • f_mode:文件的訪問模式(如只讀、只寫、讀寫)。
  • f_count:引用計數,表示有多少進程引用了這個文件,所以真正的文件關閉指的是引用計數為0的時候
  • 文件屬性存儲于結構體中,文件的內容存在緩沖區中。

文件操作的實質

從文件描述符到內核實現,文件操作的核心機制依賴于 fd_array[]file_struct

文件描述符的使用流程

每當一個進程打開文件時,內核會為文件分配一個文件描述符(fd)。這個文件描述符將作為 fd_array[] 數組的索引,指向一個 file 結構體。具體的流程如下:

  1. 文件打開:進程通過 open() 系統調用請求打開一個磁盤中的文件文件。內核會分配一個新的文件描述符(fd),并在 fd_table[] 中為該進程創建一個指向該文件的 file 結構體,屬性存于結構體,內容存于結構體指向的緩沖區中。

馮諾依曼體系中,CPU不直接與硬件交互,所以需要通過內存來交互,緩沖區在內存中形成。對文件內容做任何操作,都必須先把文件加載到內核對應的文件緩沖區內,從磁盤到內存的拷貝。

  1. 文件讀寫:通過 read()write() 系統調用,進程會通過文件描述符訪問 file 結構體中的數據,并對文件進行操作。read()本質就是內核到用戶空間的拷貝函數。
  2. 文件關閉:當文件操作完成時,進程通過 close() 系統調用關閉文件。內核會減少文件描述符表中 file 結構體的引用計數,若引用計數為 0,則釋放該文件描述符的資源。
通過文件描述符與 file 結構體的映射

文件描述符實際上是一個索引,它將用戶空間的文件 I/O 操作映射到內核空間的 file 結構體。進程每次對文件進行讀寫操作時,都會通過文件描述符查找對應的 file 結構體,然后通過 file 中的操作指針(f_op)調用具體的文件操作函數,如 read(), write()flush()

文件操作的效率
  • 緩沖機制:Linux 內核使用緩沖區來提升文件 I/O 的效率。文件數據首先被寫入內核緩沖區,只有緩沖區滿了或程序顯式調用 flush 操作時,數據才會寫入磁盤。這樣可以減少磁盤 I/O 的頻率。
  • 文件操作鎖:內核使用鎖來同步文件操作,確保多個進程對同一文件的訪問不會引發沖突。

結論

通過深入分析 FILE 結構體、task_struct 中的 file_struct 以及 fd_array[] 數組的關系,我們能夠更清晰地理解 Linux 系統中文件操作的底層機制。文件描述符作為用戶空間與內核空間的橋梁,file 結構體封裝了對文件的訪問接口,而內核通過文件描述符表、緩沖區機制和文件操作鎖等技術,保證了高效且可靠的文件 I/O 操作。

編程語言的可移植性

編程語言的可移植性指的是程序能否在不同的平臺或操作系統上順利運行。語言的設計、標準庫的實現以及對底層硬件的抽象都直接影響著程序的可移植性。

C 語言的可移植性

C 語言作為一種接近硬件的低級編程語言,直接與操作系統的底層交互。由于各個操作系統有不同的系統調用,C 語言的標準庫為不同平臺提供了相對一致的接口,使得 C 語言具備一定的可移植性。

不過,C 語言標準庫的實現也可能因操作系統而異。比如,Windows 和 Linux 都有 C 語言的實現,但它們的文件 I/O 操作部分會有所不同,Windows 可能使用 CreateFile(),而 Linux 使用 open()。為了增強 C 語言的可移植性,開發者常常通過條件編譯來區分不同操作系統下的實現。

例如,在 Windows 和 Linux 上都需要實現文件操作的代碼:

#ifdef _WIN32
#include <windows.h>
HANDLE hFile = CreateFile("log.txt", GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
#else
#include <fcntl.h>
#include <unistd.h>
int fd = open("log.txt", O_CREAT | O_WRONLY | O_APPEND, 0666);
#endif

通過使用預處理指令 #ifdef#endif,程序可以根據不同操作系統選擇不同的文件打開方式,從而增加跨平臺的可移植性。

語言的可移植性?

除了 C 語言,其他高級編程語言(如 C++、Java、Python、Go、PHP)也通過各自的標準庫和虛擬機來增強跨平臺的可移植性。

  • C++:C++ 通過標準庫(如 STL)提供了一套跨平臺的接口,使得程序能在不同操作系統上編譯和運行。然而,當涉及到直接與操作系統底層交互時,C++ 仍然需要依賴平臺特定的系統調用和 API。
  • Java:Java 提供了 Java 虛擬機(JVM),使得 Java 程序可以在不同的操作系統上運行。JVM 會屏蔽底層系統的差異,使得 Java 代碼具有良好的可移植性。Java 的字節碼可以在任何實現了 JVM 的操作系統上運行。
  • Python:Python 通過封裝了平臺特定的調用接口,提供了跨平臺的標準庫,如 ossys 等。Python 程序員通常不需要關心底層操作系統的細節,Python 會處理這些差異。
  • Go:Go 語言內置對多平臺的支持,編譯器可以直接生成不同操作系統和架構的二進制文件,從而確保 Go 程序具有較高的可移植性。
  • PHP:PHP 是一種主要用于 Web 開發的語言,它通過 Web 服務器(如 Apache、Nginx)和平臺無關的接口(如數據庫驅動)使得 PHP 程序具有一定的可移植性。

所以語言的移植性可以總結為:語言在底層庫中的使用系統調用的函數針對不同的系統會將系統調用部分更改,更換為不同操作系統的系統調用(條件編譯來解決)。

如此在上層使用語言的時候不會感受到差異,因為只是使用語言的語法,底層庫的差異在語言層面進行屏蔽,增加了語言的可移植性。

語言增加可移植性讓更多人愿意去使用,增加市場占有率。

不可移植性的原因?

  1. 操作系統依賴:

不同的操作系統有不同的API和系統調用。例如,Linux和windows的文件操作、內存管理、線程處理等API不同。如果現在有一個程序,在編寫的時候直接調用了某個操作系統特有的API,它在其他操作系統上就無法工作。必須將調用特有API更換為要在上面執行的操作系統的API才可以正常運行。

  1. 硬件依賴:

不同平臺使用的編譯器可能會有不同的行為,或者某些編輯器不支持某些特性。例如,C++中某些編譯器特性只在特定的編譯器中有效,導致代碼在其他平臺或編輯器中無法運行。

重定向

文件描述符的分配規則

當進程打開文件時,操作系統會分配一個最小的未使用文件描述符。例如:

int fd = open("example.txt", O_RDONLY);

如果文件描述符 3 未被占用,則 fd 將被賦值為 3。

重定向

重定向的核心原理在于操作文件描述符。文件描述符在file_struct中的數組中存放管理,通過改變文件描述符的指向,我們可以將輸入或輸出流重定向到文件、設備或其他流。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h> // 包含close函數的聲明int main() {// 關閉標準輸出文件描述符1close(1);// 打開(或創建)一個名為"myfile"的文件,以只寫方式打開// 如果文件不存在則創建,權限設置為644int fd = open("myfile", O_WRONLY | O_CREAT, 00644);if (fd < 0) {// 如果打開文件失敗,輸出錯誤信息并返回1perror("open");return 1;}// 輸出文件描述符printf("fd: %d\n", fd);// 刷新標準輸出緩沖區,確保輸出立即顯示fflush(stdout);// 關閉文件描述符close(fd);// 程序正常退出exit(0);
}

已知文件描述符的分配規則和重定向的原理,那么通過以上代碼理解。先關閉fd = 1的文件,也就是標準輸出流文件。此時再打開文件時就會按照文件描述符的分配規則,將新打開的文件描述符設置為按照順序最小的下標,也就是剛關閉fd = 1。然后當使用printf進行打印的時候,該函數默認的拷貝到的文件fd1,本來是向顯示屏進行打印,實際上因為新文件的占用,將內容拷貝進行新文件中。

這就是重定向,數組的下標不變,更改文件描述符的指針指向。

使用 dup2() 系統調用

在 Linux 中,dup2() 系統調用用于復制一個文件描述符,并將其指向另一個指定的文件描述符。這對于實現輸入輸出的重定向非常有用。

函數原型:

int dup2(int oldfd, int newfd);
  • oldfd:現有的文件描述符。
  • newfd:目標文件描述符。

功能:

  • oldfd 指向的文件復制到 newfd
  • 如果 newfd 已經打開,則先關閉它。
  • 返回新的文件描述符 newfd,如果出錯則返回 -1

示例代碼:

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>int main() {// 打開文件,獲取文件描述符int fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);if (fd == -1) {perror("打開文件失敗");return 1;}// 將標準輸出重定向到文件if (dup2(fd, STDOUT_FILENO) == -1) {perror("重定向標準輸出失敗");close(fd);return 1;}// 關閉原始文件描述符close(fd);// 現在 printf 的輸出將寫入 output.txtprintf("這行文本將被寫入到 output.txt 文件中。\n");return 0;
}

在上述示例中:

  • 我們首先使用 open() 打開 output.txt 文件,并獲取文件描述符 fd
  • 然后,使用 dup2() 將標準輸出(STDOUT_FILENO)重定向到 output.txt 文件。
  • 關閉原始的文件描述符 fd
  • 之后,所有通過 printf() 輸出的內容都會寫入 output.txt 文件,而不是顯示器。

在 minishell 中添加重定向功能

#include <iostream>
#include <ctype.h>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstring>
#include <unordered_map>
#include <sys/stat.h>
#include <fcntl.h>#define COMMAND_SIZE 1024      // 命令行最大長度
#define FORMAT "[%s@%s %s]# " // 提示符格式// ================== 全局數據結構聲明 ==================
// 1. 命令行參數表
#define MAXARGC 128
char *g_argv[MAXARGC]; // 存儲解析后的命令行參數
int g_argc = 0;        // 參數個數// 2. 環境變量表
#define MAX_ENVS 100
char *g_env[MAX_ENVS]; // 存儲環境變量
int g_envs = 0;        // 環境變量數量// 3. 別名映射表(當前代碼未完整實現)
std::unordered_map<std::string, std::string> alias_list;// 4. 重定向相關配置
#define NONE_REDIR 0    // 無重定向
#define INPUT_REDIR 1   // 輸入重定向 <
#define OUTPUT_REDIR 2  // 輸出重定向 >
#define APPEND_REDIR 3 // 追加重定向 >>int redir = NONE_REDIR;      // 記錄當前重定向類型
std::string filename;        // 重定向文件名// ================== 輔助函數聲明 ==================
// [省略部分環境獲取函數...]// ================== 環境初始化 ==================
void InitEnv() {extern char **environ;// 從父進程復制環境變量到g_env數組for(int i = 0; environ[i]; i++) {g_env[i] = strdup(environ[i]); // 使用strdup復制字符串g_envs++;}// 設置新環境變量(示例)g_env[g_envs++] = strdup("HAHA=for_test");g_env[g_envs] = NULL;// 更新進程環境變量for(int i = 0; g_env[i]; i++) {putenv(g_env[i]);}environ = g_env; // 替換全局environ指針
}// ================== 重定向處理核心函數 ==================
void TrimSpace(char cmd[], int &end) {// 跳過連續空白字符while(isspace(cmd[end])) end++;
}void RedirCheck(char cmd[]) {// 開始前先將文件操作的信息初始化redir = NONE_REDIR;filename.clear();int start = 0;int end = strlen(cmd)-1;// 從命令末尾向前掃描尋找重定向符號while(end > start) {if(cmd[end] == '<') { // 輸入重定向cmd[end] = '\0';  // 截斷命令字符串end++;TrimSpace(cmd, end); // 跳過空格redir = INPUT_REDIR;filename = cmd + end;break;}else if(cmd[end] == '>') {// 判斷是>>還是>if(end > 0 && cmd[end-1] == '>') { // 追加重定向cmd[end-1] = '\0'; // 截斷命令字符串end++; // 移動到>后的位置redir = APPEND_REDIR;} else { // 普通輸出重定向cmd[end] = '\0';end++;redir = OUTPUT_REDIR;}// 這時end在最后的運算符后面,然后用TrimSpace向后查找文件開頭字母TrimSpace(cmd, end);filename = cmd + end; // end為文件名開頭字母位置,直接cmd定位到文件名部分break;}else {end--; // 繼續向前掃描}}
}// ================== 命令執行 ==================
int Execute() {pid_t id = fork();if(id == 0) { // 子進程int fd = -1;switch(redir) {case INPUT_REDIR:fd = open(filename.c_str(), O_RDONLY);dup2(fd, STDIN_FILENO); // 重定向標準輸入break;case OUTPUT_REDIR:fd = open(filename.c_str(), O_CREAT|O_WRONLY|O_TRUNC, 0666);dup2(fd, STDOUT_FILENO); // 重定向標準輸出break;case APPEND_REDIR:fd = open(filename.c_str(), O_CREAT|O_WRONLY|O_APPEND, 0666);dup2(fd, STDOUT_FILENO);break;default: // 無重定向不做處理break;}if(fd != -1) close(fd); // 關閉不再需要的文件描述符execvp(g_argv[0], g_argv); // 執行程序exit(EXIT_FAILURE); // exec失敗時退出}// 父進程等待子進程int status = 0;waitpid(id, &status, 0);lastcode = WEXITSTATUS(status); // 記錄退出狀態return 0;
}// ================== 主循環 ==================
int main() {InitEnv(); // 初始化環境變量while(true) {PrintCommandPrompt(); // 打印提示符char commandline[COMMAND_SIZE];if(!GetCommandLine(commandline, sizeof(commandline))) continue;RedirCheck(commandline); // 重定向解析if(!CommandParse(commandline)) continue; // 命令解析if(CheckAndExecBuiltin()) continue; // 內建命令Execute(); // 執行外部命令}return 0;
}

總結

通過深入探討文件描述符(fd)的使用,以及如何在 C 語言中實現文件的重定向功能,我們可以更好地理解 Linux 系統文件 I/O 的工作原理。掌握這些概念和技術,對于編寫高效、可靠的系統級程序具有重要意義。

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

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

相關文章

3.8 AI驅動的市場調研革命:從數據采集到競品策略生成的閉環實踐指南

AI驅動的市場調研革命:從數據采集到競品策略生成的閉環實踐指南 引言:智能時代的高效市場洞察 Forrester研究顯示,使用AI輔助市場調研可使數據采集效率提升8倍,策略生成速度加快4倍。本文以GitHub Sentinel、LanguageMentor為案例,揭示如何構建AI增強型市場分析體系,實現…

AF3 MmcifObject類解讀

AlphaFold3 中 MmcifObject類 是 解析 mmCIF 文件的核心數據結構,用于存儲解析后的蛋白質結構信息,包含PDB 頭部信息、Biopython 解析的結構、鏈序列信息等。 下面代碼包含 Monomer 、AtomSite、ResiduePosition、ResidueAtPosition、 MmcifObject以及ParsingResult數據類的…

網絡安全 “免疫力”:從人體免疫系統看防御策略

在當今數字化時代&#xff0c;網絡安全已變得至關重要。每天&#xff0c;我們的網絡系統都面臨著來自各方的威脅&#xff0c;就像人體時刻暴露在各種病原體中一樣。今天&#xff0c;我們就來聊聊網絡安全與人體免疫系統的奇妙聯系&#xff0c;看看從免疫系統中能汲取哪些構建強…

滾動彈幕案例

滾動彈幕案例 一、需求 1.頁面上漂浮字體大小不一、顏色不一&#xff0c;從左向右滾動的彈幕&#xff1b; 2.底部中間有一個發送功能&#xff0c;可以發送新的彈幕&#xff1b; 3.底部的發送部分可以向下收起和彈出。 二、html <div class"container"><…

【wiki知識庫】08.添加用戶登錄功能--后端SpringBoot部分

目錄 一、今日目標? 二、SpringBoot后端實現 2.1 新增UserLoginParam 2.2 修改UserController 2.3 UserServiceImpl代碼 2.4 創建用戶上下文工具類 2.5?通過token校驗用戶&#xff08;重要&#xff09; 2.6 創建WebMvcConfig 2.7 用戶權限校驗攔截器 一、今日目標 上…

AI大模型的技術突破與傳媒行業變革

性能與成本&#xff1a;AI大模型的“雙輪驅動” 過去幾年&#xff0c;AI大模型的發展經歷了從實驗室到產業化的關鍵轉折。2025年初&#xff0c;以DeepSeek R1為代表的模型在數學推理、代碼生成等任務中表現超越國際頭部產品&#xff0c;而訓練成本僅為傳統模型的幾十分之一。這…

C++字符串處理指南:從基礎操作到性能優化——基于std::string的全面解析

博主將從C標準庫中的 std::string 出發&#xff0c;詳細探討字符串的處理方法&#xff0c;涵蓋常見操作、性能優化和實際應用場景。以下內容將圍繞std::string 的使用展開&#xff0c;結合代碼示例進行說明。 一、std::string 的基本操作 1.1 創建與初始化 std::string 提供了…

3.【線性代數】——矩陣乘法和逆矩陣

三 矩陣乘法和逆矩陣 1. 矩陣乘法1.1 常規方法1.2 列向量組合1.3 行向量組合1.4 單行和單列的乘積和1.5 塊乘法 2. 逆矩陣2.1 逆矩陣的定義2.2 奇異矩陣2.3 Gauss-Jordan 求逆矩陣2.3.1 求逆矩陣 ? \Longleftrightarrow ?解方程組2.3.2 Gauss-Jordan求逆矩陣 1. 矩陣乘法 1.…

深入了解 Oracle 正則表達式

目錄 深入了解 Oracle 正則表達式一、正則表達式基礎概念二、Oracle 正則表達式語法&#xff08;一&#xff09;字符類&#xff08;二&#xff09;重復限定符&#xff08;三&#xff09;邊界匹配符&#xff08;四&#xff09;分組和捕獲 三、Oracle 正則表達式函數&#xff08;…

用python寫一個聊天室程序

下面是一個簡單的基于Socket的Python聊天室程序示例&#xff0c;包括服務器端和客戶端&#xff1a; 服務器端代碼&#xff1a; import socket import threadingdef handle_client(client, address):print(f"New connection from {address}")while True:msg client…

在nodejs中使用RabbitMQ(六)sharding消息分片

RabbitMQ 的分片插件&#xff08;rabbitmq_sharding&#xff09;允許將消息分布到多個隊列中&#xff0c;這在消息量很大或處理速度要求高的情況下非常有用。分片功能通過將消息拆分到多個隊列中來平衡負載&#xff0c;從而提升消息處理的吞吐量和可靠性。它能夠在多個隊列之間…

1.7 AI智能體實戰指南:從單任務自動化到企業級智能體集群架構

AI智能體實戰指南:從單任務自動化到企業級智能體集群架構 一、智能體技術演進:從腳本工具到認知革命的跨越 1.1 三代智能體能力對比 能力維度第一代(規則驅動)第二代(機器學習)第三代(LLM驅動)任務理解固定模式匹配統計模式識別語義推理與邏輯鏈分解環境適應需人工重寫…

Github 2025-02-14 Java開源項目日報 Top10

根據Github Trendings的統計,今日(2025-02-14統計)共有10個項目上榜。根據開發語言中項目的數量,匯總情況如下: 開發語言項目數量Java項目10C#項目1Guava: 谷歌Java核心庫 創建周期:3725 天開發語言:Java協議類型:Apache License 2.0Star數量:49867 個Fork數量:10822 次…

C++17中的clamp函數

一、std::clamp() 其實在前面簡單介紹過這個函數&#xff0c;但當時只是一個集中的說明&#xff0c;為了更好的理解std::clamp的應用&#xff0c;本篇再詳細進行闡述一次。std::clamp在C17中其定義的方式為&#xff1a; template< class T > constexpr const T& cl…

WEB安全--SQL注入--常見的注入手段

一、聯表查詢&#xff1a; 1.1原理&#xff1a; 當payload參數被后端查詢語句接收到時&#xff0c;其中的非法語句通過union關聯顯示出其他的數據 1.2示例&#xff1a; #payload: -1 and union select 1,2,database()--#query: $sqlselect * from users where id-1 and union …

QT筆記——QPlainTextEdit

文章目錄 1、概要2、文本設計2.1、設置文本2.1、字體樣式&#xff08;大小、下劃線、加粗、斜體&#xff09; 1、概要 QPlainTextEdit 是 Qt 框架中用于處理純文本編輯的控件&#xff0c;具有輕量級和高效的特點&#xff0c;以下是它常見的應用場景&#xff1a; 文本編輯器&am…

【D2】神經網絡初步學習

總結&#xff1a;學習了 PyTorch 中的基本概念和常用功能&#xff0c;張量&#xff08;Tensor&#xff09;的操作、自動微分&#xff08;Autograd&#xff09;、正向傳播、反向傳播。通過了解認識LeNet 模型&#xff0c;定義神經網絡類&#xff0c;熟悉卷積神經網絡的基本結構和…

DeepSeek處理自有業務的案例:讓AI給你寫一份小眾編輯器(EverEdit)的語法著色文件

1 DeepSeek處理自有業務的案例&#xff1a;讓AI給你寫一份小眾編輯器(EverEdit)的語法著色文件 1.1 背景 AI能力再強&#xff0c;如果不能在企業的自有業務上產生助益&#xff0c;那基本也是一無是處。將企業的自有業務上傳到線上訓練&#xff0c;那是腦子進水的做法&#xff…

DeepSeek教unity------MessagePack-05

動態反序列化 當調用 MessagePackSerializer.Deserialize<object> 或 MessagePackSerializer.Deserialize<dynamic> 時&#xff0c;二進制數據中存在的任何值都將被轉換為基本值&#xff0c;即 bool、char、sbyte、byte、short、int、long、ushort、uint、ulong、…

C++入門之《拷貝構造函數》詳解

拷貝構造函數是構造函數的一個重載 拷貝構造函數是特殊的構造函數&#xff0c;用于基于已存在對象創建新對象。比如定義一個 Person 類&#xff1a; class Person { private:std::string name;int age; public:Person(const std::string& n, int a) : name(n), age(a…