toshi.txtを対応する表として書き換えてtoshi.csvにする方法。

```

    |TOKYO|155|1|　　　　　TOKYO,155,1,
    |NAGOYA|108|3|　　→   NAGOYA,108,3,
    
```

このように書き換えを行うプログラミングを教えてください。  
"|"を","に書き換えればいいのは分かるのですがうまくいきません。  
プログラミングは全くの初心者なのでよろしくお願いします。

```

    #define _CRT_SECURE_NO_WARNINGS
    #include <stdio.h>
    
    int main()
    {
        char line[100];   // ファイルから読み込んだ行を格納用
        FILE *in_file, *out_file; // ファイルポインタ格納用
        int i;
    
        printf("Change a text file toshi.txt to csv file. \n");
    
        in_file = fopen("toshi.txt", "r"); 
        if (in_file == NULL) {
            printf("fopen_in error");
            return 0;
        }
        out_file = fopen("toshi.csv", "w"); 
        if (out_file == NULL) {
            printf("fopen_out error");
            return 0;
        }
    
        while (fgets(line, 100, in_file) != NULL){
            i = 1;
            while (line[i] != '\n'){
                switch (line[i]){ 
                case'|':
                    line[i]=',';
                    break;
                    if (line[i] != '|'){ line[i] = line[i]; }
                }
    
                i++;
            }
            fputc(line[i], out_file);
        }
    
        fclose(in_file);
        fclose(out_file);
        return 0;
    }
    
```
CC BY-SA 3.0
2015-01-24T11:04:50.387
5203
2015-01-25T08:48:15.217
2015-01-24T14:53:45.663
76
7756
question
c csv
テキストファイルをcsv形式に書き換えるプログラミングを教えてください。
以下の様に変更すればだいたい質問者の想定している動作かと思います。  
（CSVとして厳密なものではないですけど）

```

    while (fgets(line, sizeof line, in_file) != NULL){
        for (i=1; line[i] != '\n'; ++i){//forにまとめました。行末には改行が存在しないといけない。
            if(line[i] == '|')//条件が１つだけなのでifに直しました
                line[i]=',';
            fputc(line[i], out_file);
        }
        fputc('\n', out_file);//行末に改行を出力する必要があります。
    }
    
```
CC BY-SA 3.0
2015-01-24T12:29:57.343
5211
2015-01-24T12:29:57.343
5044
5203
answer
sedで変換するとかどうでしょう？  
プログラミングではないですが

```

    $ sed -e 's/|/,/g' toshi.txt > toshi.csv
    
```
CC BY-SA 3.0
2015-01-25T01:29:44.117
5229
2015-01-25T01:29:44.117
4715
5203
answer
# 前置き

**switch-case文のbreakについて:**

```

    case'|':
        line[i]=',';
        break;
        if (line[i] != '|'){ line[i] = line[i]; }
    
```

caseに書かれているbreakは「このswitch文から抜ける」ことを意味します。  
つまりbreak文の後に書かれているif文は、絶対に実行されません。  
この様に絶対に実行されることがない文を`デッドコード (dead code)`と呼んだりします。

**文末かどうかの判断:**

```

    while (line[i] != '\n'){
    
```

この処理で文末(改行位置)までループするという処理を行っていますが、toshi.txtの最後の行の後に`\n`(改行)が無かった場合にループが止まらない可能性があります。

```

    // 最後の行に改行があるケース
    |TOKYO|155|1|↓
    |NAGOYA|108|3|↓
    [EOF]
    
    // 最後の行に改行がないケース
    |TOKYO|155|1|↓
    |NAGOYA|108|3|[EOF]
    
```

※[EOF]:end of file、ファイルの終端

以下の様に文字列終端を表すNULL文字(`'\0'`)かどうかもチェックしましょう。

```

    // 文字が改行('\n')でもNULL文字('\0')でもない間、ループする
    while (line[i] != '\n' && line[i] != '\0') {
    
```

# 本題

**'|'から','への置換処理について:**

変換後のCSVの終端に','(以下カンマ)を付けない処理について考えます。

```

    |TOKYO|155|1| → TOKYO,155,1
    
```

行頭の`|`は、ループを1から開始することで無視できます。  
行末の`|`は、次の文字が`\n`か`\0`の時に何も出力しなければ良さそうです。  
(次の文字(`line[i+1]`)が、`\n`か`\0`以外の場合にカンマへ置き換える)

```

    while (fgets(line, sizeof line, in_file) != NULL) {
        i = 1;
    
        // 文字が改行('\n')でもNULL文字('\0')でもない間、ループする
        while (line[i] != '\n' && line[i] != '\0') {
            switch (line[i]) {
            case '|':
                // '|'の時、次の文字(i+1)が改行('\n')、NULL文字('\0')でない場合
                // ','を出力して'|'から置き換える
                if (line[i + 1] != '\n' && line[i + 1] != '\0') {
                    fputc(',', out_file);
                }
                break;
            default:
                // '|'以外の文字はそのまま出力する
                fputc(line[i], out_file);
                break;
            }
    
            i++;
        }
    
        // '|' → ','の置換が終わったら改行して次の行へ
        fputc('\n', out_file);
    }
    
```

**sizeof演算子について:**

大雑把に説明すると、sizeof演算子は配列を渡してあげると配列のサイズを返してくれます。  
(今回の場合`line[100]`の`100`を返します)  
これを使用すると、サイズを100から変更する必要が出た時にソースコードを1か所だけ修正すれば済むようになります。

sizeof演算子については別途勉強してみてください。

**似ている処理を関数にまとめる:**

ところで先ほどのコードですが、`○ != '\n' && ○ !=
'\0'`という文末かどうかを判定している処理が2か所にあります。これらは`isEndOfLine(○)`という関数にまとめられそうです。

```

    ・・・
        while (fgets(line, sizeof line, in_file) != NULL) {
            i = 1;
    
            // 文字が文末でない間、ループする
            while (isEndOfLine(line[i]) == 0) {
                switch (line[i]) {
                case '|':
                    // '|'の時、次の文字(i+1)が文末でない場合
                    // ','を出力して'|'から置き換える
                    if (isEndOfLine(line[i + 1]) == 0) {
                        fputc(',', out_file);
                    }
                    break;
                default:
                    // '|'以外の文字はそのまま出力する
                    fputc(line[i], out_file);
                    break;
                }
    
                i++;
            }
    
            // '|' → ','の置換が終わったら改行して次の行へ
            fputc('\n', out_file);
        }
    
        fclose(in_file);
        fclose(out_file);
        return 0;
    }
    
    /**
     * 文字が文末かどうかを判定する
     * @param c 文字
     * @return 文字が改行('\n')かNULL文字('\0')の場合、1を返す。それ以外の場合、0を返す
     */
    int isEndOfLine(char c) {
        if (c == '\n' || c == '\0') {
            return 1;
        }
        return 0;
    }
    
```
CC BY-SA 3.0
2015-01-25T03:58:36.010
5233
2015-01-25T08:48:15.217
2020-06-17T08:14:45.997
-1
3068
5203
answer