ページ 11

文字列のコピー

Posted: 2012年7月02日(月) 22:19
by takumi
はじめまして、C言語初心者です。

さっそくですが本題に移らせていただきます。
学校の課題なのですが、よろしくお願いします。

問)getlineで文字配列lineに読み込んだ文字列をmemに詰め込んみ最後に詰め込んだmemを表示せよ。(\0の部分は_で未使用部分は*を表示する)

例)初めに「gohann」次に「pann」と入力して終わらせると……
↓結果例↓
gohann[6]追加
gohann_************

pann[4]追加
gohann_pann_********

^Z
データ数=4
↑ここまで↑

ただしmemに詰め込める文字数を超えてしまったら-1を返す
例)先ほどの「pann」の後に「misoshiru」を入力
↓結果例↓
pann[4]追加
gohann_pann_********

misoshiru[9]追加
gohann_pann_********

データ数=-1
↑ここまで↑

これに対して僕が書いたのが以下の通りです。

コード:

#include	<stdio.h>
#define	MMAX	20	//memの要素数
#define	MAXLEN	20	//lineの要素数

//外部変数
char	mem[MMAX];
char	*mp=mem;

//関数宣言
int		pushst	(void);
int		getline	(char s[],	int lim);
int		delspace(char line[], int len);
int		copy(char p, char line[]);
char	*getmem(int len);
void	printmem(char mem[]);

void main(void)
{
	//宣言
	int nd=0;
	
	nd=pushst();	//関数呼び出し
	
	//結果を表示
	printf("データ数=%d\n",nd);
}

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

	return i;
}
int delspace(char line[], int len)
{
	int i=0,n;
	while(line[i]!=' ' && line[i]!='\n' && line[i]!='\t')
	{
		++i;
	}
	line[i]='\0';
		
	for(n=0; n<i; ++n)
	{
		printf("%c",line[n]);
	}
	printf("[%d]追加\n",n);
	return i;
}
char *getmem(int len)
{
	*mp+=len;
	
	if((*mp-*mem)<=MMAX)
	{
		return mp-len;
	}
	else
	{
		return NULL;
	}
}
int copy(char p, char line[])
{
	int i;
	for(i=0; i<=p; ++i)
	{
		mem[p+i]=line[i];
	}
	return 0;
}

void printmem(char mem[])
{
	int i;
	for(i=0; i<MMAX; ++i)
	{
		if('a'<=mem[i] && mem[i]<='z')
		{
			putchar(mem[i]);
		}
		else if(mem[i]=='\0')
		{
			putchar('*');
		}
		else
		{
			putchar('_');
		}
	}
	printf("\n\n");
}

int pushst(void)
{
	char	line[MAXLEN]={0};
	char	*p=0;
	int		len;
	int		n=0;
	
	while(1)
	{
		len=getline(line,MAXLEN);
		if(len==0)
		{
			return n;
		}
		len=delspace(line,len);
		p=getmem(len+1);
		
		if(p!=NULL)
		{
			copy(*p,line);
		}
		else
		{
			return -1;
		}
		
		printmem(mem);
		++n;
	}
	return n;
}
しかしこれではmemに詰め込めず、さらに文字数が超えてしまってもー1が戻らずに止まってしまいます。
どのようにすればいいでしょうか、アドバイスお願いします。

Re: 文字列のコピー

Posted: 2012年7月03日(火) 09:23
by non
元のプログラムをなるべく生かしたいとは思うのですが、各々の関数で何をしたいのか見えてきません。
すこし、コメントを入れてみませんか?そうすると、自分の考えのどこに間違いがあるか見えてくると思いますよ。

まず、45行目の mem[n]=s[n];
は、何をしたいのでしょうか?入力された文字をmemに先頭から入れているように思えますが、
あなたの考えは?

また、getmemやcopyでは何をしたいのでしょうか?

Re: 文字列のコピー

Posted: 2012年7月03日(火) 10:11
by かずま
元のプログラムをなるべく生かすとこうなりました。
何をどう勘違いしていたのか説明してください。

コード:

#include    <stdio.h>
#define MMAX    20  //memの要素数
#define MAXLEN  20  //lineの要素数
 
//外部変数
char    mem[MMAX];
char    *mp=mem;
 
//関数宣言
int     pushst  (void);
int     getline (char s[],  int lim);
int     delspace(char line[], int len);
int     copy(char *p, char line[]);      // *p <-- p
char    *getmem(int len);
void    printmem(char mem[]);
 
void main(void)
{
    //宣言
    int nd=0;
    
    nd=pushst();    //関数呼び出し
    
    //結果を表示
    printf("データ数=%d\n",nd);
}
 
//関数定義
int getline(char s[], int lim)
{
    int c,i,n;
    for(i=0; i<lim-1 && (c=getchar())!=EOF && c!='\n'; ++i)
    {
        s[i]=c;
    }
    if(c=='\n')
    {
        s[i]='\n';
        ++i;
    }
    s[i]='\0';
/*                                削除
    for(n=0; n<i; ++n)
    {
        mem[n]=s[n];
    }
*/ 
    return i;
}
int delspace(char line[], int len)
{
    int i=0,n;
    while(line[i]!=' ' && line[i]!='\n' && line[i]!='\t')
    {
        ++i;
    }
    line[i]='\0';
        
    for(n=0; n<i; ++n)
    {
        printf("%c",line[n]);
    }
    printf("[%d]追加\n",n);
    return i;
}
char *getmem(int len)
{
//  *mp+=len;                     // 削除
    if((mp-mem+len)<=MMAX)        // <-- if((*mp-*mem)<=MMAX)
    {
        mp += len;                // 追加
        return mp-len;
    }
    else
    {
        return NULL;
    }
}
int copy(char *p, char line[])    // *p <-- p
{
    int i;
    for(i=0; line[i]; ++i)        // line[i] <-- i<=p
    {
        p[i]=line[i];             // p[i] <-- mem[p+i]
    }
    line[i] = '_';                // 追加
    return 0;
}
 
void printmem(char mem[])
{
    int i;
    for(i=0; i<MMAX; ++i)
    {
        if('a'<=mem[i] && mem[i]<='z')
        {
            putchar(mem[i]);
        }
        else if(mem[i]=='\0')
        {
            putchar('*');
        }
        else
        {
            putchar('_');
        }
    }
    printf("\n\n");
}
 
int pushst(void)
{
    char    line[MAXLEN]={0};
    char    *p=0;
    int     len;
    int     n=0;
    
    while(1)
    {
        len=getline(line,MAXLEN);
        if(len==0)
        {
            return n;
        }
        len=delspace(line,len);
        p=getmem(len+1);
        
        if(p!=NULL)
        {
            copy(p,line);             // p <-- *p
        }
        else
        {
            return -1;
        }
        
        printmem(mem);
        ++n;
    }
    return n;
}

Re: 文字列のコピー

Posted: 2012年7月03日(火) 22:36
by takumi
返信が遅れてしまってすみませんでした、アドバイスありがとうございます。
おかげさまで何とかこの問題は解決することができました。

主な問題点はポインタと配列をちゃんと理解していなかったことでそれぞれの役割をごちゃまぜに使おうとしていたことだと思います。
あと例として載せていただいたコードでは未使用の部分だけでなく\0の部分も*になってしまっていたのでそこを直しました……。

しかし、この次の問題なのですが、そこだと上での考え方では\0と未使用部分が一緒として考えられているらしく\0の部分には’_’を未使用部分には’*’とすることができません。
どうしたらいいでしょうかいまいちどアドバイスください、よろしくおねがいします。

問)ポインタ変数lpを用いて各文字列の先頭を指すように先ほどのプログラムを改良せよ。

コード:

#include	<stdio.h>
#define	MMAX	20  //memの要素数
#define	MAXLEN	20  //lineの要素数
#define DMAX	4
 
//外部変数
char	mem[MMAX];
char	*mp=mem;
 
//関数宣言
char	*getmem	(int len);
void	printmem(char mem[]);
int		pushst2	(char *lp[],	int lmax);
int		getline	(char s[],		int lim);
int		delspace(char line[],	int len);
int		copy	(char *p, 		char line[]);      // *p <-- p

 
void main(void)
{
	//宣言
	int nd=0,i,n;
    char *lp[DMAX];
    
	nd=pushst2(lp,DMAX);    //関数呼び出し
    
	//結果を表示
	printf("データ数=%d\n",nd);
	for(i=0; i<nd; ++i)
	{
		printf("%s\n",lp[i]);
	}
}
 
//関数定義
int getline(char s[], int lim)
{
	int c,i,n;
	for(i=0; i<lim-1 && (c=getchar())!=EOF && c!='\n'; ++i)
	{
		s[i]=c;
	}
	if(c=='\n')
	{
		s[i]='\n';
		++i;
	}
	s[i]='\0';

	return i;
}
int delspace(char line[], int len)
{
	int i=0,n;
	while(line[i]!=' ' && line[i]!='\n' && line[i]!='\t')
	{
		++i;
	}
	line[i]='\0';
	
	for(n=0; n<i; ++n)
	{
		printf("%c",line[n]);
	}
	printf("[%d]追加\n",n);
	return i;
}
char *getmem(int len)
{
	if((mp-mem+len)<=MMAX)        // <-- if((*mp-*mem)<=MMAX)
	{
		mp += len;                // 追加
		return mp-len;
	}
	else
	{
		return NULL;
	}
}
int copy(char *p, char line[])    // *p <-- p
{
	int i;
	for(i=0; line[i]!='\0'; ++i)        // line[i] <-- i<=p
	{
		p[i]=line[i];             // p[i] <-- mem[p+i]
	}
	p[i] = '\0';                // 追加
	return 0;
}

void printmem(char mem[])
{
	int i;
	for(i=0; i<MMAX; ++i)
	{
		if('a'<=mem[i] && mem[i]<='z')
		{
			putchar(mem[i]);
		}
		else if(mem[i]=='\0')
		{
			putchar('_');
		}
		else
		{
			putchar('*');
		}
	}
	printf("\n\n");
}

int pushst2(char *lp[], int lmax)
{
	char	line[MAXLEN];
	char	*p=0;
	int		len;
	int		n=0;
    int		i=0;
    
    
	while(1)
	{		
		len=getline(line,MAXLEN);
		if(len==0)
		{
			return n;
		}
		len=delspace(line,len);
		p=getmem(len+1);
		if(p!=NULL)
		{
			copy(p,line);
			if(i<DMAX)
			{
				lp[i]=p;
			}
			else
			{
				printf("ポインタ不足\n");
				return -1;
			}
			printmem(mem);
			++i;
		}
		else
		{
			printf("mem不足\n");
			return -1;
		}
		++n;
	}
	return n;
}

Re: 文字列のコピー

Posted: 2012年7月03日(火) 23:55
by かずま
takumi さんが書きました: あと例として載せていただいたコードでは未使用の部分だけでなく\0の部分も*になってしまっていたのでそこを直しました……。
すみません。私のプログラムの 86行目の line = '_'; は p = '_'; の間違いでした。

printmem の中の for文の中は

コード:

    if (mem[i])
        putchare(mem[i]);
    else
        putchar('*');
で十分です。