2016-10-14 13 views
2

私はUNIXシステムプログラミングを学んでいます。 UNIX用のシンプルなシェルアプリケーションを作成しています(OS X Yosemite ver 10.10.5を使用していますが、Xcodeを使用しています)。私はCで少し経験しましたが、あまりありませんでした。Cでユニコード文字列を比較

ユーティリティプログラムは正常に動作し、Unicode文字を出力します(ただし、lsはXcodeコンソールでは代わりに「????」を表示しますが、デバッガ自体の問題であるようです)。

私はちょっとした研究をして、strcmp()もバイトを比較して最後にゼロバイトを探している限り、うまく動作することがわかりました。読み込み入力は、バイトを読み込むだけでOKです。

また、ユニコード文字列にnullバイトが含まれていないことも読んでいます。しかし、 `strcmp()を実行すると、入力によってはEXC_BAD_ACCESSが発生します。

コード:

読書のユーザー入力:

char* readCommand(void) { 
    int buffer_size = LINE_BUFFER_SIZE; 
    char *buffer = malloc(sizeof(char) * buffer_size); 
    int position = 0; 
    int character; 

    if(!buffer) 
    { 
     fprintf(stderr, "readCommand failed: memory allocation error"); 
     exit(ALLOCATION_ERROR); 
    } 

    while (1) { 
     character = getchar(); 
     if(character == EOF || character == '\n') 
     { 
      buffer[position] = '\0'; 
      char* cmd = buffer; 
      free(buffer); 
      return cmd; 
     } 
     else { 
      buffer[position] = character; 
     } 
     if(++position >= sizeof(buffer)) 
     { 
      buffer_size += LINE_BUFFER_SIZE; 
      buffer = realloc(buffer, sizeof(char) * buffer_size); 
      if(!buffer) { 
       fprintf(stderr, "readCommand failed: memory reallocation error"); 
       free(buffer); 
       exit(ALLOCATION_ERROR); 
      } 
     } 
    } 
    return NULL; 
} 

スプリット引数:strcmpのがどこにあるのです

int split_string_quotes(char* source, char** argv, size_t arg_count) 
{ 
    enum split_states state = DULL; 
    char* p, *word_start = NULL; 
    int character; 
    int argc = 0; 
    for(p = source; argc < arg_count && *p != '\0'; p++) 
    { 
     character = (unsigned char) *p; 
     switch (state) { 
      case DULL: 
       if(isspace(character)) 
       { 
        continue; 
       } 
       if(character == '"') 
       { 
        state = IN_STRING; 
        word_start = p+1; 
        continue; 
       } 
       state = IN_WORD; 
       word_start = p; 
       continue; 

      case IN_WORD: 
       if(isspace(character)) 
       { 
        state = DULL; 
        *p = 0; 
        argv[argc++] = word_start; 
       } 
       continue; 

      case IN_STRING: 
       if(character == '"') 
       { 
        state = DULL; 
        *p = 0; 
        argv[argc++] = word_start; 
       } 
       continue; 
     } 
    } 

    if(state != DULL && argc < arg_count) 
    { 
     argv[argc++] = word_start; 
    } 
    argv[argc] = NULL; 
    return argc; 
} 

int shell_execute(char **args) 
{ 
    for(int i = 0; i < 3; i++) 
    { 
     if(strcmp(args[0], commands[i]) == 0) 
     { 
      return (*standardFuncs[i])(args); 
     } 
    } 
    shell_launch(args); 
    return 0; 
} 

そして、メインループ

char* current_dir = malloc(sizeof(char)*PATH_MAX); 
    char* args[MAX_ARGS]; 
    char* command; 
    printf("dolphinShell (c) Alex Kale 2016\n"); 
    while (1) 
    { 
     getwd(current_dir); 
     printf("dsh: %s-> ", current_dir); 
     command = readCommand(); 
     printf("%s\n", command); 
     split_string_quotes(command, args, MAX_ARGS); 
     if(shell_execute(args) == -1) break; 
    } 
    free(current_dir); 
    return 0; 

ので、問題はいくつかのUnicode文字列は、私は仕事の罰金を入力してEXC_BAD_ACCESSを引き起こすことがないということですが、私はфывпфвыапыを入力すると、例えば、それが壊れます。私はこの問題はargs[0]へのアクセスにあると思いますが、ここでは、デバッガの出力です:

Printing description of args: 
    (char **) args = 0x00007fff5fbff900 
    *args char * 0x101800a00 0x0000000101800a00 
    Printing description of *(*(args)): 
    (char) **args = '\xd1' 

は、だから、args[0]が空であることを考えて、それが空でありますか?それとも、すべてゼロで混乱していますか?

私は本当に混乱しています。私は研究に多くの時間を費やしており、ここにこだわっているようです。

私もwchar_twcscmp()を使ってみましたが、execvp()とうまく動作せず、問題を解決しません。私もここにgcc -Wall -Wextraを試してきた

は出力です:

main.c:53:26: warning: comparison of integers of different signs: 'int' and 
     'size_t' (aka 'unsigned long') [-Wsign-compare] 
    for(p = source; argc < arg_count && *p != '\0'; p++) 
        ~~~~^~~~~~~~~~ 
main.c:92:30: warning: comparison of integers of different signs: 'int' and 
     'size_t' (aka 'unsigned long') [-Wsign-compare] 
    if(state != DULL && argc < arg_count) 
         ~~~~^~~~~~~~~~ 
main.c:124:23: warning: comparison of integers of different signs: 'int' and 
     'unsigned long' [-Wsign-compare] 
     if(++position >= sizeof(buffer)) 
      ~~~~~~~~~~^~~~~~~~~~~~~~~ 
main.c:180:18: warning: unused parameter 'args' [-Wunused-parameter] 
int dHelp(char **args) 
       ^
main.c:203:18: warning: unused parameter 'args' [-Wunused-parameter] 
int dExit(char **args) 
       ^
main.c:210:14: warning: unused parameter 'argc' [-Wunused-parameter] 
int main(int argc, const char** argv) 
      ^
main.c:210:33: warning: unused parameter 'argv' [-Wunused-parameter] 
int main(int argc, const char** argv) 
           ^
7 warnings generated. 

しかし、私はそれはケース(私が間違っているなら、私を修正)だとは思いません。

+0

http://www.joelonsoftware.com/articles/Unicode.html – Biffen

+0

"*私は少しの調査をして、strcmp()もバイトと比較していればうまく動作するはずであることを知りました。最後にゼロバイトを探します。読み込み入力もちょうどバイト読み込みのように "+" - これはAnsiまたはUTF-8の 'char'ベースの文字列を使用して入力を読み込んでいる場合にのみ当てはまります。例えば。代わりにUTF-16/32として読み込まれるUnicode文字列では機能しません。それらを処理するには 'wchar_t'が必要です。 –

+0

また、 'args [0]'のデバッガ出力に何も表示されません。 'args [0]'(つまり '* args')は、配列中の最初の' char * '文字列ポインタであり、出力ではNULLではありません。 '** args'(つまり' args [0] [0] ')は配列の最初の文字列の最初の' char'であり、NULLでもありません。 '0xD1'は、UTF-8のUnicode'ф'文字の最初の**バイト**です(その2番目の**バイト**は '0x84 'です)。 'фывпфвыапы'の文字はすべてUTF-8で2バイトずつ使用されます(したがって' strlen( "фывпфвыапы")はUTF-8では20であり、10ではありません)。 –

答えて

2

示されたコードには複数のバグがあります。

 char* cmd = buffer; 
     free(buffer); 
     return cmd; 

これは、削除されたcharバッファへのポインタを返します。このポインタを引き続き使用すると、未定義の動作が発生します。

 if(++position >= sizeof(buffer)) 

bufferは、char *である。これは、

 if(++position >= sizeof(char *)) 

ハードウェアプラットフォームに応じて4バイトまたは8バイトのいずれかになります。これは、4または8バイトよりも大きくなるたびに、バッファを不必要にサイズ変更します。

sizeof()malloc -edバッファのサイズを示していると思われるようです。それはしません。

最後に、全体的なアプローチは、大きなコードを書いて、それが正しく動作するかどうかを調べることです。これは間違ったやり方です。小さな関数を書く必要があります。 1つの行をバッファに読み込むものなど。試して。それが動作することを確認します。今すぐあなたがそれが動作することを知っている、あなたの全体のプログラムの次の小さな部分に移動して書き込む。

+0

私はそれを得る。ありがとう!しかし、私はそれにユニコード文字列を渡すまではうまくいきました。私がしていることにもっと注意を払うべきだと思います。私にとって大きなレッスン。 –

+0

ああ、そうではありませんでした。私はユニコードをテストする直前にフリー(バッファ)を追加しました。 –

0

コードの大きなバグは、入力を読み取る方法です。この部分を見てください:ここで

if(character == EOF || character == '\n') 
{ 
    buffer[position] = '\0'; 
    char* cmd = buffer; 
    free(buffer); 
    return cmd; 
} 

あなたが必要として、あなたは、ゼロとbuffer終端されています。次に、cmdにのメモリと同じメモリを指すように割り当て、バッファを解放し、のメモリを解放したにポインタを戻します。この後で、返されたポインタを何かのために使用すると、それは許可されず、何かが起こる可能性があります。メモリはどこかで再利用することができます。アクセス違反を受けるか、近くに火山が噴出する可能性があります。

ポインタを別の変数に代入しても、メモリのコピーは作成されず、同じ場所を指し示すだけです。内容の使用を停止する前にメモリを解放することはできません。これがあなたの問題を引き起こしている可能性が最も高いです。

+0

私はそれを得る。それは本当にそれでなければならない、私がそれを逃した方法を知らない。私は後で確認してみるが、それは問題だと確信している。 –