我正在嘗試在具有以下形狀的檔案末尾輸入一行 "1 :1 :1 :1" ,因此在某些時候該檔案的末尾可能有一個換行符,并且按順序要執行我必須處理的操作,所以我想出了以下解決方案:轉到檔案末尾并向后退 1 個字符(我猜是 Linux 作業系統中換行符的長度),閱讀該字符,如果不是換行符,則插入一個,然后插入整行,否則插入該行,這是該解決方案在 C 上的翻譯:
int insert_element(char filename[]){
elements *elem;
FILE *p,*test;
size_t size = 0;
char *buff=NULL;
char c='\n';
if((p = fopen(filename,"a"))!=NULL){
if(test = fopen(filename,"a")){
fseek(test,-1,SEEK_END );
c= getc(test);
if(c!='\n'){
fprintf(test,"\n");
}
}
fclose(test);
p = fopen(filename,"a");
fseek(p,0,SEEK_END);
elem=(elements *)malloc(sizeof(elements));
fflush(stdin);
printf("\ninput the ID\n");
scanf("%d",&elem->id);
printf("input the adress \n");
scanf("%s",elem->adr);
printf("innput the type \n");
scanf("%s",elem->type);
printf("intput the mark \n");
scanf("%s",elem->mark);
fprintf(p,"%d :%s :%s :%s",elem->id,elem->adr,elem->type,elem->mark);
free(elem);
fflush(stdin);
fclose(p);
return 1;
}else{
printf("\nRrror while opening the file !\n");
return 0;
}
}
您可能會注意到整個程式取決于換行符的長度(1 個字符“\n”)所以我想知道是否有最佳方式,換句話說,適用于所有作業系統
uj5u.com熱心網友回復:
看來您已經了解附加到檔案的基礎知識,所以我們只需要弄清楚檔案是否已經以換行符結尾。
在理想情況下,您會跳到檔案末尾,備份一個字符,讀取該字符,然后查看它是否匹配'\n'. 像這樣的東西:
FILE *f = fopen(filename, "r");
fseek(f, -1, SEEK_END); /* this is a problem */
int c = fgetc(f);
fclose(f);
if (c != '\n') {
/* we need to append a newline before the new content */
}
盡管這可能適用于 Posix 系統,但它不適用于許多其他系統。問題的根源在于系統在文本檔案中分隔和/或終止行的許多不同方式。在 C 和 C 中,'\n'是一個特殊值,它告訴文本模式輸出例程執行插入換行符所需的任何操作。'\n'同樣,文本模式輸入例程將在回傳讀取的資料時將每個換行符轉換為。
在 Posix 系統(例如,Linux)上,換行符由換行符 (LF) 指示,該換行符在 UTF-8 編碼文本中占據一個位元組。所以編譯器只是定義'\n'為換行符,然后輸入和輸出例程在文本模式下不必做任何特殊的事情。
在某些較舊的系統(如舊的 MacOS 和 Amiga)上,換行符可能由回車符 (CR) 表示。許多 IBM 大型機使用稱為 EBCDIC 的不同字符編碼,它們沒有 LF 或 CR 的直接映射,但它們確實有一個稱為下一行 (NL) 的特殊控制字符。甚至有些系統(如 VMS、IIRC)不使用文本檔案的流模型,而是使用可變長度記錄來表示每一行,因此換行符本身是隱式的,而不是由特定的控制字符標記。
其中大部分是您在現代系統上不會面臨的挑戰。Unicode 添加了更多的換行約定,但很少有軟體以一般方式支持它們。
剩下的主要換行約定是組合 CR LF。CR LF 的挑戰在于它是兩個控制字符,但是 C i/o 函式必須使它們在程式員看來好像它們是單個字符'\n'。這對于流式輸入或輸出文本沒什么大不了的。但這使得在檔案中查找變得難以定義。這讓我們回到了有問題的路線:
fseek(f, -1, SEEK_END);
在換行符由兩個字符序列(如 LF CR)指示的系統上從末尾備份“一個字符”是什么意思?我們真的希望 i/o 系統必須掃描整個檔案以便fseek(和ftell)弄清楚如何理解偏移量嗎?
C 標準的人下注。 在文本模式下,offset 引數fseek只能是0或之前呼叫回傳的值ftell。因此,帶有負偏移量的有問題的呼叫無效。(在 Posix 系統上,無效的呼叫fseek可能會起作用,但標準并不要求它這樣做。)
另請注意,Posix 將 LF 定義為行終止符而不是分隔符,因此不以 a 結尾的非空文本檔案'\n'應該不常見(盡管確實會發生)。
對于更便攜的解決方案,我們有兩種選擇:
以文本模式閱讀整個檔案,記住您最近閱讀的字符是否為
'\n'.此選項效率極低,因此除非您只是偶爾或僅對短檔案執行此操作,否則我們可以排除這種情況。
以二進制模式打開檔案,從末尾向后查找幾個位元組,然后讀取到末尾,記住您最后讀取的內容是否是有效的換行符序列。
如果我們在以二進制模式打開檔案時
fseek不支持原點,這可能是一個問題。SEEK_END是的,C 標準說支持是可選的。但是,大多數實作都支持它,因此我們將保持此選項處于打開狀態。由于檔案將以二進制模式讀取,因此輸入例程不會將平臺的換行符序列轉換為
'\n'. 我們需要一個狀態機來檢測超過一個位元組長的換行序列。讓我們做一個簡單的假設,即換行符是 LF 或 CR LF。在后一種情況下,我們不關心CR,因此我們可以簡單地從末尾備份一個位元組并測驗它是否是LF。
哦,我們必須弄清楚如何處理一個空檔案。
bool NeedsLineBreak(const char *filename) {
const int LINE_FEED = '\x0A';
FILE *f = fopen(filename, "rb"); /* binary mode */
if (f == NULL) return false;
const bool empty_file = fseek(f, 0, SEEK_END) == 0 && ftell(f) == 0;
const bool result = !empty_file ||
(fseek(f, -1, SEEK_END) == 0 && fgetc(f) == LINE_FEED);
fclose(f);
return result;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/463150.html
