ページ 11

プログラミング言語C 第2版(K&R)の内容について

Posted: 2015年8月07日(金) 19:54
by cookierinon
C言語初心者です。プログラミング言語C 第2版(K&R)の内容について質問させていただきます。
P42演習1-21 ブランクの列を同じスペーシングを行う最小の数のタブおよびブランクで置き換えるプログラムentabを書け。detabと同じタブストップを使え。タブ・ストップに達するのに、タブあるいは単一のブランクのいずれかで十分なときに、どちらを使うべきか?
※detabとは演習1-20の、入力されたタブを、次のタブ・ストップまでのスペースをうめる適当な数のブランク(空白)で置き換えるプログラム

これは要するに、空白が必要な数だけ?あればタブに置き換わるようにコードを書け、と解釈したのですが、

1 2 3
と入力(数字の後ろに数字の数だけ空白)した際に、
1 2   3
となってしまいます。置き換えたタブの長さが固定なのがおかしいのだけはわかります。
以下のコードがおかしいのか、そもそも問いの解釈がおかしいのか、タブ・ストップの解釈がおかしいのか、ご指摘お願いします。

コード:

/* 空白をタブ+空白に置きかえる */
#include <stdio.h>
#define TABSTOP 4

int main(void){
	int c, i;
	int countspace;	/* 連続する空白の数 */
	int startspace; /* 連続する1以上の空白において最初の空白の文字位置 */
	countspace = startspace = 0;

	printf("文章を入力してください。\n");

	for(i=0; (c= getchar()) != EOF; i++){
		if(c == ' '){
			if(countspace == 0)
				startspace = i;
			++countspace;
		}
		else{
			while(0 < countspace){
					/* 空白がタブで置き換わる */
					if(countspace >= TABSTOP - (startspace % TABSTOP)){
						putchar('\t');
						countspace -= TABSTOP - (startspace % TABSTOP);
						if(0 < countspace)	/* まだ空白がある */
							startspace += TABSTOP - (startspace % TABSTOP);
					}
					/* 空白がタブで置き換わらない */
					else{
						while(0 < countspace){
							putchar(' ');
							--countspace;
						}
					}
			}
			putchar(c);
		}
	}
	return 0;
}

Re: プログラミング言語C 第2版(K&R)の内容について

Posted: 2015年8月07日(金) 20:22
by みけCAT
cookierinon さんが書きました:1 2 3
と入力(数字の後ろに数字の数だけ空白)した際に、
1 2   3
となってしまいます。置き換えたタブの長さが固定なのがおかしいのだけはわかります。
嘘はつかないでください。提示されたコードで全角スペースが追加されるはずはありません。
数字の後ろに数字の数だけ空白を入れた入力「1S2SS3SSS」の場合出力は1S2TS3TS
提示された「1S2SS3SSSSSSS」を入力した場合の出力は1S2TS3TTS
となりました。(半角空白をSで、タブをTで置き換えました)
cookierinon さんが書きました:以下のコードがおかしいのか、そもそも問いの解釈がおかしいのか、タブ・ストップの解釈がおかしいのか、ご指摘お願いします。
出力結果を表示するソフトウェアによるタブ・ストップがプログラムのタブ・ストップとずれている(おかしくはない)のではないかと思います。

Re: プログラミング言語C 第2版(K&R)の内容について

Posted: 2015年8月07日(金) 20:25
by みけCAT
cookierinon さんが書きました:以下のコードがおかしいのか、そもそも問いの解釈がおかしいのか、タブ・ストップの解釈がおかしいのか、ご指摘お願いします。
コードもおかしいですね。
文字位置が改行でリセットされないため、例えば

コード:

12
  34
を入力した時の出力がおかしいです。

Re: プログラミング言語C 第2版(K&R)の内容について

Posted: 2015年8月07日(金) 21:03
by cookierinon
みけCATさん、回答ありがとうございます。

すいません、もちろん嘘をつくつもりはないですが、質問の文章を打ってる時には確かに半角スペースを叩いたはずですが全角スペースになっていました。
正しくは、
1S2SS3SSS
と入力すると出力が
1S2SSSSSS3SSSSSSS
です。(出力で表示される空白が空白かタブなのかはよくわかりません)
みけCAT さんが書きました: 出力結果を表示するソフトウェアによるタブ・ストップがプログラムのタブ・ストップとずれている(おかしくはない)のではないかと思います。
環境はVisual Studio 2010なのですが、
出力結果を表示するソフトウェア…コマンドプロンプト
プログラム…vs2010
という解釈でよろしいでしょうか?
その場合、私が修正するのはvs2010の設定なのでしょうか?

Re: プログラミング言語C 第2版(K&R)の内容について

Posted: 2015年8月07日(金) 21:25
by みけCAT
cookierinon さんが書きました:すいません、もちろん嘘をつくつもりはないですが、質問の文章を打ってる時には確かに半角スペースを叩いたはずですが全角スペースになっていました。
正しくは、
1S2SS3SSS
と入力すると出力が
1S2SSSSSS3SSSSSSS
です。(出力で表示される空白が空白かタブなのかはよくわかりません)
1タブ=8スペースでシミュレーションするとこの出力と一致しますね。
嘘というきつい表現をしてすみませんでした。
cookierinon さんが書きました:
みけCAT さんが書きました: 出力結果を表示するソフトウェアによるタブ・ストップがプログラムのタブ・ストップとずれている(おかしくはない)のではないかと思います。
環境はVisual Studio 2010なのですが、
出力結果を表示するソフトウェア…コマンドプロンプト
プログラム…vs2010
という解釈でよろしいでしょうか?
いいえ。
出力結果を表示するソフトウェア…知りません(多分コマンドプロンプトなのでしょう)
プログラム…提示された「空白をタブ+空白に置きかえる」プログラム
です。
cookierinon さんが書きました:その場合、私が修正するのはvs2010の設定なのでしょうか?
いいえ。
Windows 7のコマンドプロンプトにタブ幅を設定する項目は見当たらなかったので、
出力をテキストファイルにリダイレクトし、適当なテキストエディタ(メモ帳はNG、サクラエディタなど)で確認するといいでしょう。

Re: プログラミング言語C 第2版(K&R)の内容について

Posted: 2015年8月07日(金) 22:18
by cookierinon
みけCAT さんが書きました:
cookierinon さんが書きました: Windows 7のコマンドプロンプトにタブ幅を設定する項目は見当たらなかったので、
出力をテキストファイルにリダイレクトし、適当なテキストエディタ(メモ帳はNG、サクラエディタなど)で確認するといいでしょう。
さしあたっての問題はここのご指摘の箇所、これC言語とは関係ないからちょっと勉強してきます。正しい出力を確認次第解決!押します。

Re: プログラミング言語C 第2版(K&R)の内容について

Posted: 2015年8月08日(土) 01:13
by cookierinon
サクラエディタを使用したところコマンドプロンプトとは出力の結果が違うことを確認しました。

ご指摘くださった改行の処理を修正(22行目)、その際に同じ処理を書くことになったので関数entabを
誤りなどございませんでしょうか?

コード:

/* 空白をタブ+空白に置きかえる */
#include <stdio.h>
#define TABSTOP 4

/* 空白以外を入力したとき空白をタブ+空白に置きかえる関数 entab */
void entab(int countspace, int startspace);

int main(void){
	int c, i;
	int countspace;	/* 連続する空白の数 */
	int startspace; /* 連続する1以上の空白において最初の空白の文字位置 */
	countspace = startspace = 0;

	printf("文章を入力してください。\n");

	for(i=0; (c= getchar()) != EOF; i++){
		if(c == ' '){
			if(countspace == 0)
				startspace = i;
			++countspace;
		}
		else if(c == '\n'){
			entab(countspace, startspace);
            putchar(c);
			i = 0;
		}

		else{
			entab(countspace, startspace);
			putchar(c);
		}
	}
	return 0;
}

void entab(int countspace, int startspace){
	while(0 < countspace){
					/* 空白がタブで置き換わる */
					if(countspace >= TABSTOP - (startspace % TABSTOP)){
						putchar('\t');
						countspace -= TABSTOP - (startspace % TABSTOP);
						if(0 < countspace)	/* まだ空白がある */
							startspace += TABSTOP - (startspace % TABSTOP);
					}
					/* 空白がタブで置き換わらない */
					else{
						while(0 < countspace){
							putchar(' ');
							--countspace;
						}
					}
			}
	return;
}

Re: プログラミング言語C 第2版(K&R)の内容について

Posted: 2015年8月08日(土) 01:21
by みけCAT
cookierinon さんが書きました:誤りなどございませんでしょうか?
誤りはあります。
entabした後にcountspaceをリセットしていないので、例えば入力

コード:

1 2 3
に対する出力がおかしいです。
自分でテストしてから質問しましたか?
cookierinon さんが書きました:ご指摘くださった改行の処理を修正(22行目)、その際に同じ処理を書くことになったので関数entabを
同じ処理を書く必要はありません。
No: 1のコードの36行目(putchar)の直後で、cが'\n'だったらiを-1 (直後のi++の影響を受けるため) にすればいいでしょう。
オフトピック
そういえば、入力にタブが含まれる場合の処理が無いなあ…そこまで考えるべきかなあ…

Re: プログラミング言語C 第2版(K&R)の内容について

Posted: 2015年8月08日(土) 02:29
by cookierinon
みけCATさん、回答ありがとうございます。
みけCAT さんが書きました: 同じ処理を書く必要はありません。
No: 1のコードの36行目(putchar)の直後で、cが'\n'だったらiを-1 (直後のi++の影響を受けるため) にすればいいでしょう。
これは盲点でした。ちなみに『iを-1にすればいい』を『iを-1すればいい』と誤読して小一時間悩んでいました。危うく質問するところでした。
みけCAT さんが書きました: 誤りはあります。
entabした後にcountspaceをリセットしていないので、例えば入力

コード:

1 2 3
に対する出力がおかしいです。
自分でテストしてから質問しましたか?
痛いところをつかれたといいますか…今回の設問においてのみVisual Studioでビルド→デバッグでちゃちゃっとテストできず初めて触るサクラエディタを使わないといけないので
テストを怠ってしまいました。
ただcountspaceをリセットしていない~というのがよくわかりません。NO.7の49行目の記述で必ずcountspaceはリセット(0になる)されると思うのですが…
(タブの入力に関しましては設問が設問なのでタブを入力しない前提で考えていました。エラー処理のような形で必要なら書き直します。)

Re: プログラミング言語C 第2版(K&R)の内容について

Posted: 2015年8月08日(土) 07:31
by みけCAT
cookierinon さんが書きました:ただcountspaceをリセットしていない~というのがよくわかりません。NO.7の49行目の記述で必ずcountspaceはリセット(0になる)されると思うのですが…
entab関数のcountspaceを0にしても、main関数のcountspaceは0になりません。
entab関数のcountspaceは保存されないので、main関数のcountspaceを0にしないといけません。
cookierinon さんが書きました:(タブの入力に関しましては設問が設問なのでタブを入力しない前提で考えていました。エラー処理のような形で必要なら書き直します。)
cookierinon さんが書きました:P42演習1-21 ブランクの列を同じスペーシングを行う最小の数のタブおよびブランクで置き換えるプログラムentabを書け。detabと同じタブストップを使え。タブ・ストップに達するのに、タブあるいは単一のブランクのいずれかで十分なときに、どちらを使うべきか?
設問に「タブは入力されない」という条件は無さそうです。
タブがあったら、その次の文字を行頭として扱えばいいでしょう。

Re: プログラミング言語C 第2版(K&R)の内容について

Posted: 2015年8月08日(土) 08:56
by cookierinon
みけCATさん、回答ありがとうございます。
みけCAT さんが書きました: entab関数のcountspaceを0にしても、main関数のcountspaceは0になりません。
entab関数のcountspaceは保存されないので、main関数のcountspaceを0にしないといけません。
仮引数と実引数の名前を変えないとこんなことにも気づけないとは…

修正2回目のコードです。NO.1のコードに37行目からのif文を足しただけなのでこれで問題ないはずなのですが…

コード:

/* 空白をタブ+空白に置きかえる */
#include <stdio.h>
#define TABSTOP 4

int main(void){
	int c, i;
	int countspace;	/* 連続する空白の数 */
	int startspace; /* 連続する1以上の空白において最初の空白の文字位置 */
	countspace = startspace = 0;

	printf("文章を入力してください。\n");

	for(i=0; (c= getchar()) != EOF; i++){
		if(c == ' '){
			if(countspace == 0)
				startspace = i;
			++countspace;
		}
		else{
			while(0 < countspace){
				/* 空白がタブで置き換わる */
				if(countspace >= TABSTOP - (startspace % TABSTOP)){
					putchar('\t');
					countspace -= TABSTOP - (startspace % TABSTOP);
					if(0 < countspace)	/* まだ空白がある */
						startspace += TABSTOP - (startspace % TABSTOP);
				}
				/* 空白がタブで置き換わらない */
				else{
					while(0 < countspace){
						putchar(' ');
						--countspace;
					}
				}
			}
			putchar(c);
			if(c == '\n' || c == '\t')
				i = -1;	/* 改行・タブで文字位置をリセット */
		}
	}
	return 0;
}

Re: プログラミング言語C 第2版(K&R)の内容について

Posted: 2015年8月08日(土) 11:02
by みけCAT
cookierinon さんが書きました:修正2回目のコードです。NO.1のコードに37行目からのif文を足しただけなのでこれで問題ないはずなのですが…
大きな問題は無さそうだと思います。
入力の最後に改行が入らず空白で終わると誤動作するので、cがEOFかの判定をループ開始時ではなく、36行目のputcharの直前で行うようにするとさらに良くなるでしょう。

Re: プログラミング言語C 第2版(K&R)の内容について

Posted: 2015年8月08日(土) 14:18
by かずま
cookierinon さんが書きました:修正2回目のコードです。NO.1のコードに37行目からのif文を足しただけなのでこれで問題ないはずなのですが…
そのプログラムに、そのソースを入力として与えると
不適切だと思われるところの多くにタブが挿入されます。例えば、

コード:

@   @   @   if(c ==@'\n' ||@c == '\t')
@   @   @   @   i =@-1;@/* 改行・タブで文字位置をリセット */
タブを @ で表しています。
2個以上連続するスペースをタブに置き換えたほうがよいでしょう。

私なら、次のように書きます。

コード:

#include <stdio.h>

#define TABSTOP 4

int main(void)
{
    int c;
    int col = 0;         /* columns % TABSTOP */
    int countspace = 0;  /* count of spaces */

    while ((c = getchar()) != EOF) {
        if (col == TABSTOP) {
            col = 0;
            if (countspace > 1) {
                putchar('\t');
                countspace = 0;
            }
        }
        if (c == ' ')
            countspace++;
        else {
            for (; countspace > 0; countspace--)
                putchar(' ');
            putchar(c);
        }
        if (c == '\n' || c == '\t')
            col = 0;
        else
            col++;
    }
    return 0;
}
割り算は使っていません。

Re: プログラミング言語C 第2版(K&R)の内容について

Posted: 2015年8月08日(土) 16:27
by cookierinon
みけCATさん、回答ありがとうございます。
みけCAT さんが書きました:cがEOFかの判定をループ開始時ではなく、36行目のputcharの直前で行うようにするとさらに良くなるでしょう。
私はputchar関数の使い方は、教科書どおりのfor, while文の()内に記述する方法しかわからないのですが、どのように書けばよろしいのでしょう?

かずまさん、回答ありがとうございます。
変数colを使えばぐっと簡単になるのがわかりました。
私はこれまで、
col == 3 のとき、空白をタブに『置き換えなければならない』と思っていたのですが『そのまま空白でもいい』のですよね?
設問の
タブ・ストップに達するのに、タブあるいは単一のブランクのいずれかで十分なときに、どちらを使うべきか?
というのが何を意図しているかさっぱりわからずスルーしていたのですが提供してくださったコードを読んでようやく理解できました(たぶん)。

ただ、この記述に関しましては何を伝えてくださろうとしているかさっぱりわかりませんでした。
かずま さんが書きました: そのプログラムに、そのソースを入力として与えると
不適切だと思われるところの多くにタブが挿入されます。例えば、

コード:

@   @   @   if(c ==@'\n' ||@c == '\t')
@   @   @   @   i =@-1;@/* 改行・タブで文字位置をリセット */
タブを @ で表しています。

Re: プログラミング言語C 第2版(K&R)の内容について

Posted: 2015年8月08日(土) 16:55
by みけCAT
かずま さんが書きました:私なら、次のように書きます。

コード:

#include <stdio.h>

#define TABSTOP 4

int main(void)
{
    int c;
    int col = 0;         /* columns % TABSTOP */
    int countspace = 0;  /* count of spaces */

    while ((c = getchar()) != EOF) {
        if (col == TABSTOP) {
            col = 0;
            if (countspace > 1) {
                putchar('\t');
                countspace = 0;
            }
        }
        if (c == ' ')
            countspace++;
        else {
            for (; countspace > 0; countspace--)
                putchar(' ');
            putchar(c);
        }
        if (c == '\n' || c == '\t')
            col = 0;
        else
            col++;
    }
    return 0;
}
入力

コード:

123     90
に対する出力がおかしい(3の後に1個スペースが入らないといけないのに、抜けている)ように思えます。
cookierinon さんが書きました:
みけCAT さんが書きました:cがEOFかの判定をループ開始時ではなく、36行目のputcharの直前で行うようにするとさらに良くなるでしょう。
私はputchar関数の使い方は、教科書どおりのfor, while文の()内に記述する方法しかわからないのですが、どのように書けばよろしいのでしょう?
普通に書けばいいです。

コード:

/* 空白をタブ+空白に置きかえる */
#include <stdio.h>
#define TABSTOP 4

int main(void){
	int c, i;
	int countspace;	/* 連続する空白の数 */
	int startspace; /* 連続する1以上の空白において最初の空白の文字位置 */
	countspace = startspace = 0;

	printf("文章を入力してください。\n");

	for(i=0; ; i++){
		c = getchar();
		if(c == ' '){
			if(countspace == 0)
				startspace = i;
			++countspace;
		}
		else{
			while(0 < countspace){
				/* 空白がタブで置き換わる */
				if(countspace >= TABSTOP - (startspace % TABSTOP)){
					putchar('\t');
					countspace -= TABSTOP - (startspace % TABSTOP);
					if(0 < countspace)	/* まだ空白がある */
						startspace += TABSTOP - (startspace % TABSTOP);
				}
				/* 空白がタブで置き換わらない */
				else{
					while(0 < countspace){
						putchar(' ');
						--countspace;
					}
				}
			}
			if(c == EOF)
				break;
			putchar(c);
			if(c == '\n' || c == '\t')
				i = -1;	/* 改行・タブで文字位置をリセット */
		}
	}
	return 0;
}
オフトピック
ちなみに、
B・M・カーニハン/D・M・リッチー著 石田晴久訳 (1989) 「プログラミング言語C 第2版 ANSI規格準拠」 共立出版株式会社
の20ページに

コード:

#include <stdio.h>

/* 入力を出力に複写;第1版 */
main()
{
    int c;

    c = getchar();
    while (c != EOF) {
        putchar(c);
        c = getchar();
    }
}
という、putcharをfor, while文の()内以外に記述しているプログラムがあります。

Re: プログラミング言語C 第2版(K&R)の内容について

Posted: 2015年8月08日(土) 17:23
by かずま
みけCAT さんが書きました: 入力

コード:

123     90
に対する出力がおかしい(3の後に1個スペースが入らないといけないのに、抜けている)ように思えます。
その通りです。ご指摘ありがとうございます。

コード:

            if (countspace > 1) {
                putchar('\t');
上の 2行を次のように訂正します。

コード:

            if (countspace >= 1) {
			    if (countspace == 1)
                    putchar(' ');
				else
                    putchar('\t');

Re: プログラミング言語C 第2版(K&R)の内容について

Posted: 2015年8月08日(土) 18:25
by cookierinon
みけCATさん、かずまさん、回答ありがとうございます。
特にみけCATさんには何がわからないのかわからない状態からここまで導いていただきました。