2015-09-06 49 views
0

我在調試Vigenere的密碼C中的實現時遇到了問題。使用文件輸入(-f標誌)時文件包含少於6個字符(+ 1 EOF)時出現錯誤,它吐出一些隨機字符的數量以及預期的輸入,我不知道爲什麼這是,雖然我懷疑它與我的問題的第二部分有關,即使用fread()時,我注意到這是Vigenere cipher in C

if(fread(fcontents, fsize, sizeof(char), file) != 1) {...} 

將沒有問題跑,而這

if(fread(fcontents, sizeof(char), fsize, file) != 1) {...} 

不工作(即導致fread()返回1,並觸發它下面的錯誤處理代碼),根據here的回答,我希望是另一種方式,但我可能會誤解某些內容。

我的完整代碼如下:

#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#include <ctype.h> 
#include <unistd.h> 

#define ENC 0 //Encrypt mode 
#define DEC 1 //Decrypt mode 
#define INP 0 //Commandline input mode 
#define FLE 1 //File input mode 

typedef struct { 
    char *password; 
    char *file_name; 
    char *input; 
    int edmode; 
    int ifmode; 
} options; 

void string_clean(char *source) 
{ 
    char *i = source; 
    char *j = source; 

    while(*j != 0) { 
     *i = *j++; 
     if(*i != ' ' && (isupper(*i) || islower(*i))) 
      i++; 
    } 

    *i = 0; 
} 

char *ftostr(char *file_name) //Takes a file name as input and returns a char* to the files contents, returns NULL pointer on faliure. Allocated string must be freed after use by parent function to prevent memory leaks. 
{ 
    FILE *file; 
    long int fsize; 
    char *fcontents; 

    if(!(file = fopen(file_name, "r"))) { 
     fprintf(stderr, "Error opening file \"%s\"!\n", file_name); 
     return NULL; 
    } 

    fseek(file, 0L, SEEK_END); 
    fsize = ftell(file); 
    rewind(file); 

    if(!(fcontents = malloc((fsize + 1) * sizeof(char)))) { 
     fclose(file); 
     fprintf(stderr, "Error allocating memory!"); 
     return NULL; 
    } 

    if(fread(fcontents, fsize, sizeof(char), file) != 1) { //suspected buggy line 
     fclose(file); 
     free(fcontents); 
     fprintf(stderr, "Error copying file to memory!\n"); 
     return NULL; 
    } 

    fclose(file); 
    return fcontents; 
} 

options parse_opts(int argc, char *argv[]) 
{ 
    int c; 
    options args; 

    args.edmode  = ENC; //enable encrypt mode by default 
    args.ifmode  = INP; //enable commandline input mode by default 
    args.file_name = NULL; 
    args.password = NULL; 
    args.input  = NULL; 
    opterr   = 0; 

    while((c = getopt(argc, argv, "dep:i:f:")) != -1) { 
     switch(c) { 
      case 'e': 
       args.edmode  = ENC; 
       break; 
      case 'd': 
       args.edmode  = DEC; 
       break; 
      case 'p': 
       args.password = optarg; 
       break; 
      case 'i': 
       args.input  = optarg; 
       args.ifmode  = INP; 
       break; 
      case 'f': 
       args.file_name = optarg; 
       args.ifmode  = FLE; 
       break; 
      case '?': 
       if(optopt == 'f' || optopt == 'p' || optopt == 'i') 
        fprintf(stderr, "Option -%c requires an argument.\n", optopt); 
       else if(isprint(optopt)) 
        fprintf(stderr, "Unknown option `-%c'.\n", optopt); 
       else 
        fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt); 

       fprintf(stderr, "Usage: %s (-f file_name || -i input) -p password [options]\n" 
           "Optional: -e -d\n", argv[0]); 
       exit(-1); 
     } 
    } 

    return args; 
} 

char *vigenere_dec(char cipher_text[], char cipher[]) 
{ 
    char *plain_text; 

    string_clean(cipher_text); 
    string_clean(cipher); 

    int plain_text_len = strlen(cipher_text); 
    int cipher_len = strlen(cipher); 

    if(!(plain_text = malloc((plain_text_len + 1) * sizeof(char)))) 
     return 0; 

    for(int i = 0; i < cipher_len; i++) { 
     if(isupper(cipher[i])) 
      cipher[i] -= 'A'; 
     else if(islower(cipher[i])) 
      cipher[i] -= 'a'; 
    } 

    for(int i = 0, j = 0; i < plain_text_len; i++, j++) { 
     if(j == cipher_len) 
      j = 0; 

     if(isupper(cipher_text[i])) 
      cipher_text[i] -= 'A'; 
     else if(islower(cipher_text[i])) 
      cipher_text[i] -= 'a'; 

     plain_text[i] = ((cipher_text[i] - cipher[j]) % 26); 
     if(plain_text[i] < 0) 
      plain_text[i] += 26; 
     plain_text[i] += 'A'; 
    } 
    return plain_text; 
} 

char *vigenere_enc(char plain[], char cipher[]) 
{ 
    char *cipher_text; 

    string_clean(plain); 
    string_clean(cipher); 

    int plain_len = strlen(plain); 
    int cipher_len = strlen(cipher); 

    if(plain_len == 0 || cipher_len == 0) 
     return NULL; 

    if(!(cipher_text = malloc((plain_len + 1) * sizeof(char)))) 
     return NULL; 

    for(int i = 0; i < cipher_len; i++) { 
     if(isupper(cipher[i])) 
      cipher[i] -= 'A'; 
     else if(islower(cipher[i])) 
      cipher[i] -= 'a'; 
    } 

    for(int i = 0, j = 0; i < plain_len; i++, j++) { 
     if(j == cipher_len) 
      j = 0; 

     if(isupper(plain[i])) 
      plain[i] -= 'A'; 
     else if(islower(plain[i])) 
      plain[i] -= 'a'; 

     cipher_text[i] = ((plain[i] + cipher[j]) % 26) + 'A'; 
    } 
    return cipher_text; 
} 

int main(int argc, char *argv[]) 
{ 
    options args; 
    char *output_text = NULL; 

    args = parse_opts(argc, argv); 

    if(args.password == NULL) { 
     fprintf(stderr, "Password uninitialised!\n"); 
     exit(-1); 
    } 

    if(args.input == NULL && args.file_name == NULL) { 
     fprintf(stderr, "Input stream uninitialised!\n"); 
     exit(-1); 
    } 

    if(args.ifmode == INP) { 
     if(args.edmode == ENC) 
      output_text = vigenere_enc(args.input, args.password); 
     else if(args.edmode == DEC) 
      output_text = vigenere_dec(args.input, args.password); 
    } else if(args.ifmode == FLE) { 
     if(!(args.input = ftostr(args.file_name))) 
      return -1; 
     if(args.edmode == ENC) 
      output_text = vigenere_enc(args.input, args.password); 
     else if(args.edmode == DEC) 
      output_text = vigenere_dec(args.input, args.password); 
     free(args.input); 
    } 

    puts(output_text); 
    free(output_text); 

    return 0; 
} 
+1

'EOF'不是'char'。請提供[mcve](注意**最小**!)和「不起作用」是沒有錯誤的描述。 – Olaf

+0

「啓用文件模式」是什麼意思?除了要求的MCVE外,還請提供樣品輸入,預期產量和實際產量。 – kaylum

+1

您引用了另一個問題,但未能看到'fread()'返回讀取**項**的數量。所以如果你交換'size'和'count'參數,期望得到不同的結果(除非'fsize == 1')。 –

回答

1

故障原因是未結束的字符串。您允許的餘地終止字符與

if(!(plain_text = malloc(plain_text_len + 1))) // (simplified) 

但你設置後,例如

plain_text[i] += 'A'; 

你需要

plain_text[i+1] = '\0'; 

或當字符串完成結束串。

對於第二部分,您引用了另一個問題,但未能看到fread()返回讀取的項目數。因此,如果您更換sizecount參數,則會得到不同的結果(除非fsize == 1)。

所以,你既可以使用

if(fread(fcontents, fsize, 1, file) != 1) {...} 

或本

if(fread(fcontents, 1, fsize, file) != fsize) {...} 

注意我的定義改變sizeof(char)1,因爲,它是。

+0

謝謝,我覺得這件事需要很長時間才能發現! 'sizeof(char)'位是爲了清晰,因爲我不太喜歡神奇的數字。 –