質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.10%

ブランク(空白)列をタブに置き換えるには?

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,888

score 36

**問題
「ブランクの列を同じスペーシングを行う最小の数のタブおよびブランク
 で置き換えるプログラムentabを書け。」
にとりくんでいます。
下のようにプログラムを作ってみたのですが、上手く動作しません。
間違いをご指摘いただけるとありがたいです!

#include <stdio.h>

#define    MAXLINE    1000
#define    TAB_STOP    8/*タブストップは8文字ごと*/

int getline(char line[],int maxline);
void entab(char line[], char en_line[]);

main()
{
    char    line[MAXLINE];
    char    en_line[MAXLINE];
    
    while ((getline(line, MAXLINE)) > 0) {
        entab(line, en_line);
        printf("%s",en_line);
    }
    return 0;
}

int getline(char s[], int lim)
{
    int c,i;
    
    for (i = 0; i<lim-1 && (c=getchar())!=EOF && c!='\n'; i++)
        s[i] = c;
    if (c == '\n') {
        s[i] = c;
        ++i;
    }
    s[i] = '\0';
    return i;
}

void entab(char s[],char t[]) {
    
    int    i,j;/* ループカウンタ */
    int count;/* ブランクの数を格納する */
    int    step;/* タブ・ストップまでのブランクの数 */
    int    ntab;/* 置き換えるタブの数 */
    int    nblk;/* 置き換えるブランクの数 */
    
    i = 0;
    j = 0;
    while (s[i] != '\0'){
        count = 0;
        if (s[i] != ' ') {
            t[j] = s[i];
            i++;
            j++;
        }
        else {
            j = i;/*はじめてブランクが見つかったところ*/
            while (s[i] != ' ') {/*ブランク以外の文字にあたるまで*/
                ++count;
                ++i;
            }
            step = TAB_STOP - (j % TAB_STOP);/*タブストップまでのブランクの数を求める*/
            if (count < step) {/*ブランクの数よりもstepが多い*/
                while (0 < count) {
                    t[j] = ' ';/*ブランクをそのままコピーする*/
                    ++j;
                    --count;
                }
            } 
            else
            if (step <= count) {/*ブランクの数がstep以上*/
                ntab = 1 + (count -step)/TAB_STOP;
                nblk = count - step - ntab;
                while (0 < ntab) {
                    t[j] = '\t';
                    ++j;
                    --ntab;
                }
                while (0 < nblk) {
                    t[j] = ' ';
                    ++j;
                    --nblk;
                }
            }
        }
    }
    t[j] = '\0';
}

                

例えば
 "a****bc"
という文字列が入力されたとき、

  s[]  0 1 2 3| 4 5 6 7| 8
       a * * *   * b c

    ("*"はブランク、"|"はタブストップの前を表しています。簡単のためプログラムでは8文字です
     が4文字ごとにおいています。)

a~bまでのブランクの個数を数えて、4つあるとわかる。
タブストップまでの3つを、1つのタブに、
残りの1つを、ブランクにおきかえる。

というふうに考えてかいてみました。
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

0

実装例とそのテストの例です。
#include <stdio.h>
#include <string.h>

#define    MAXLINE    (100) // entab 結果の最大文字領域
#define    TAB_STOP   (8)   // タブストップは8文字ごと

// 印字可能文字な ASCII 文字だけを扱う。
// TODO: entab の結果が MAXLINE を超える時は MAXLINE より後ろはカットする。
void entab(char * str, char* entabstr) {
  int len = strlen(str);
  int entab_len = 0;   // entabstr に転送した文字数
  int space_len = 0;   // entab に転送していない SPACE の数
  int disp_len  = 0;   // str[len-1] の文字の 直前のタブストップからの表示位置 (0, 1, ... 7)

  for (int i = 0; i < len; i++) {
    // entabstr の長さがオーバーしたら、そこで処理を打ち切る。
    if (entab_len >= MAXLINE - 1) {
      break;
    }
    char c = str[i];

    if (c == '\t') {
      disp_len = 0;   // タブストップからの表示位置は 0 になる。
      space_len = 0;  // \t の前にあった SPACE は転送しない。
      entabstr[entab_len] = '\t';  // '\t' を転送する。
      entab_len++;
      continue;
    }

    if (c == ' ') {
      disp_len++;
      space_len++;   // entabstr には、すぐには転送しない。
    } else {
      disp_len++;
      // 出力していない SPACE があれば、それを entabstr に転送してから c を転送する。
      if (space_len > 0) {
    for (int s = 0; s < space_len; s++) {
      entabstr[entab_len] = ' ';
      entab_len++;
    }
    space_len = 0;
      }
      entabstr[entab_len] = c;
      entab_len++;
    }

    // 表示位置がタブストップ位置に達した時に、
    // 出力していない SPACE があれば、'\t' を転送する。
    if (disp_len % TAB_STOP == 0) {
      disp_len = 0;   // タブストップからの表示位置は 0 になる。
      if (space_len > 0) {
    entabstr[entab_len] = '\t';
    entab_len++;
    space_len = 0;
      }
    }
  }
  entabstr[entab_len] = '\0';  // 文字の終端の nil を書き込む。
}

char str_max[MAXLINE];
char str_max_1[MAXLINE + 1];
char * TEST_CASES[] = {
  /* 0 */ "11223344", "11223344",
  /* 1 */ "112233  ", "112233\t",
  /* 2 */ "1122  44", "1122  44",
  /* 3 */ "  223344", "  223344",
  /* 4 */ "11  33  ", "11  33\t",

  /* 5 */ "\t",       "\t",    // SPACE 0 + \t
  /* 6 */ "  \t",     "\t",    // SPACE 1 + \t
  /* 7 */ "       \t","\t",    // SPACE 7 + \t
  /* 8 */ "        \t","\t\t", // SPACE 8 + \t
  /* 9 */ "11\t",     "11\t",

  /* 10 */ "",         "",
  /* 11 */  str_max,    str_max,
  /* 12 */ str_max_1,  str_max,
};
void setup_testcases() {
  for (int i = 0; i < MAXLINE - 1; i++) {
    str_max[i] = 'x';
  }
  str_max[MAXLINE - 1] = '\0';

  for (int i = 0; i < MAXLINE; i++) {
    str_max_1[i] = 'x';
  }
  str_max_1[MAXLINE] = '\0';
};

void test_entab() {

  int len = sizeof(TEST_CASES) / sizeof(char *) / 2;
  char entabstr[MAXLINE];

  setup_testcases();
  for (int i = 0; i < len; i++) {
    char * str  = TEST_CASES[i * 2];
    char * ans  = TEST_CASES[i * 2 + 1];
    memset(entabstr, '*', MAXLINE);  // ダミー値で埋める。
    entab(str, entabstr);
    if (strcmp(ans, entabstr) != 0) {
      printf("#--- Error: %d:\t[%s],\t[%s] != [%s]\n", i, str, ans, entabstr);
      printf("#---      :    \t\t%d,%d\n", (int)strlen(ans), (int)strlen(entabstr));
    }
  }
}

int main(int atgc, char** argv) {
  test_entab();
  return 0;
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/03/24 14:49

    すみません…
    テストはコンパイルできて、結果が出力されたのですが
    この実装例は通りませんでした。
    Windows機でコマンドプロンプトを使っています。

    あと、テストを通したとき、
        for (int i = 0; i < len; i++) {
        ...
    のところでエラーとされました。
    ので、forループに入る前に変数iを宣言する
    ように書き換えると、通りました。
    int i;
    ...
    for (i = 0; ...) {
    ...

    キャンセル

  • 2015/03/25 00:07

    私は Mac で作業してます。
    次のように compile, 実行ができていますが。

    $ gcc --version
    Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
    Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn)
    Target: x86_64-apple-darwin14.1.0
    Thread model: posix
    $ gcc 2.c
    $ ./a.out

    キャンセル

  • 2015/03/25 07:35

    お手数かけてすみません。
    コンパイルできました。

    キャンセル

0

とりあえずで直すと下記のような感じでしょうか。(ちゃんとテストした訳ではないので、各種パターンについてはご確認ください。)
while (s[i] != ' ') {/*ブランク以外の文字にあたるまで*/
↓
while (s[i] == ' ') {/*ブランク以外の文字にあたるまで*/

ntab = 1 + (count -step)/TAB_STOP;
nblk = count - step - ntab;
↓
ntab = (i / TAB_STOP) - (j / TAB_STOP);
nblk = i - ntab * TAB_STOP;

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

まずはテストを書いてみました。
entab 関数は 何も変更しないものを仮実装してあります。
entab 関数が正しくプログラミングできた時には、テストが全て通るとおもいます。

#include <stdio.h>
#include <string.h>

#define    MAXLINE    (100)
#define    TAB_STOP   (8)  /* タブストップは8文字ごと */

// 印字可能文字な ASCII 文字だけを扱う。
// TODO: entab の結果が MAXLINE を超える時は MAXLINE より後ろはカットする。
void entab(char * str, char* entabstr) {
  // str をそのまま entabstr に copy するだけの仮実装
  int len = strlen(str);
  for (int i = 0; i < len; i++) {
    char c = str[i];
    entabstr[i] = c;
  }
  entabstr[len] = '\0';
}

char str_max[MAXLINE];
char str_max_1[MAXLINE + 1];
char * TEST_CASES[] = {
  /* 0 */ "11223344", "11223344",
  /* 1 */ "112233  ", "112233\t",
  /* 2 */ "1122  44", "1122  44",
  /* 3 */ "  223344", "  223344",
  /* 4 */ "11  33  ", "11  33\t",

  /* 5 */ "\t",       "\t",
  /* 6 */ "  \t",     "\t",
  /* 7 */ "11\t",     "11\t",
  /* 8 */ "",         "",
  /* 9 */  // str_max,    str_max,
  /* 10 */ // str_max_1,  str_max,
};
void setup_testcases() {
  for (int i = 0; i < MAXLINE - 1; i++) {
    str_max[i] = 'x';
  }
  str_max[MAXLINE - 1] = '\0';

  for (int i = 0; i < MAXLINE; i++) {
    str_max_1[i] = 'x';
  }
  str_max_1[MAXLINE] = '\0';
};

void test_entab() {

  int len = sizeof(TEST_CASES) / sizeof(char *) / 2;
  char entabstr[MAXLINE];

  setup_testcases();
  for (int i = 0; i < len; i++) {
    char * str  = TEST_CASES[i * 2];
    char * ans  = TEST_CASES[i * 2 + 1];
    memset(entabstr, '*', MAXLINE);  // ダミー値で埋める。
    entab(str, entabstr);
    if (strcmp(ans, entabstr) != 0) {
      printf("#--- Error: %d:\t%s,\t[%s] != [%s]\n", i, str, ans, entabstr);
      printf("#---      :    \t%d,%d\n", (int)strlen(ans), (int)strlen(entabstr));
    }
  }
}

int main(int atgc, char** argv) {
  test_entab();
  return 0;
}
現状では、テストを走らせると次のようになります。
$ gcc entab.c 
$ ./a.out
katoy-no-MacBook-Pro-2:cpp katoy$ ./a.out
#--- Error: 1:    112233  ,    [112233    ] != [112233  ]
#---      :        7,8
#--- Error: 4:    11  33  ,    [11  33    ] != [11  33  ]
#---      :        7,8
#--- Error: 6:          ,    [    ] != [      ]
#---      :        1,3
TEST_CASES の 1番、4番、6番が正しい entab 結果を返していない事が示されています。
(これ以外は、entab をしても文字列内容に変化が起こらない場合のテストです)

文字列長さが MAXLINE を超える時のテストケースは上ではコメントアウトしてますが、最終的には コメントを外します。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/03/18 07:20

    数日後に、このテストをパスする 私の entab() のプログラム例を投稿します、

    キャンセル

  • 2015/03/18 07:30

    ありがとうございます!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.10%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る