文字型配列の指定文字の削除について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
kaori

文字型配列の指定文字の削除について

#1

投稿記事 by kaori » 14年前

{a,b,c,x,d,x,x,e,x,f,\0}
という文字型配列があったとして、xを取り除いて{a,b,c,d,e,f,\0}という配列にしたいのです。
その場合、例えば'd'の前にはxが1つ、'e' の前にはxが3つ、'f'の前にはxが4つありますよね?
その数だけそれぞれの位置を左へずらせばxを取り除ける処理ができると思うのですが、その場合どのようにすれば実現できるでしょうか。
forでループをするというのは何となく分かるのですが、何分初心者なもので、見当がつきません。

アバター
やっくん
記事: 5
登録日時: 14年前
住所: 長崎県長崎市

Re: 文字型配列の指定文字の削除について

#2

投稿記事 by やっくん » 14年前

ずらす方法では無いですが、参考にどうぞ。
新たに配列を作って’x’以外の文字をコピーしてます。

コード:


#include <stdio.h>
#include <stdlib.h>
 
int main(void)
{
        int i;
        int cnt; // 文字数
        int p;
        char *str = "abcxdxxexf";
        char *ans;
        
        /* 'x' 以外の文字数を数える*/
        cnt = 0;
        for(i = 0; str[i] != NULL; i++){
                if(str[i] != 'x') cnt++;
        }
 
        /* 文字数分のメモリ確保 */
        ans = (char*)calloc(cnt, sizeof(char));
        if(ans == NULL){// メモリ確保できなかったら。
                printf("メモリ確保失敗\n");
                return 1;
        }
 
        /* 文字列のコピー */
        p = 0; 
        for(i = 0; i < cnt; i++){
                while(str[p] == 'x') p++; // 'x'の部分を飛ばす。
                ans[i] = str[p];
                p++;
        }
        printf("%s\n", ans);
 
        return 0;
 
}    
<出力>
abcdef

アバター
GRAM
記事: 164
登録日時: 14年前
住所: 大阪

Re: 文字型配列の指定文字の削除について

#3

投稿記事 by GRAM » 14年前

アルゴリズムを正直にコードにすると以下のようになります(Cでのコードです)

コード:

#include <stdio.h>
#include <string.h>
 
char str[] = "abcxdxxexfgxxx";
int main(){
        int counter[100];
        int numX = 0;
        int i = 0, j = 0;
 
        memset(counter,0,sizeof(counter));
        do{
                if(str[i] == 'x'){
                        ++numX;
                        continue;
                }
                counter[i] = numX;
        }while(str[i++] != '\0');
        do{
                if(counter[j])str[j-counter[j]] = str[j];
        }while(str[j++] != '\0');
        printf(str);
        return 0;
}

説明します。
まずchar str[] = 云々は大丈夫ですね。単純に文字列をchar型配列に押し込んだだけです。
具体的なアルゴリズムですが、strのn文字目(これは0から始まるとします)におけるそれまでに出たxの個数をcounter[n]に格納します
この時どうせ消えてなくなるstr[n] == 'x'の文字に対応するcounter[n]には値を何も代入しません
これは後々楽にもなります。つまりずらすべき文字にはカウンタがありxにはないのです。自動的にxの部分を上書きできます

最初のwhileループでこれを行いもう一方のwhileループでおっしゃられたように一気に文字をずらして
表示します
現在n文字目のカウンターがkならばその文字はn-k文字目にずらされるはずです
kは先に求めてあります。k == counter[n]です。

最後に終端処理が正しくされることを確かめましょう
前処理ではヌル文字にもカウンターが付けられます。
つまり後のループではヌル文字もほかの文字同様にずらされます。
よってこのコードは正しく動きます。
こんなのでどうでしょう?^^;

UN
記事: 18
登録日時: 14年前
住所: 神奈川県

Re: 文字型配列の指定文字の削除について

#4

投稿記事 by UN » 14年前

Cでの正規表現はめんどくさかったりしますからね。
すごく適当にですが書いてみました。

コード:

 
#define	GETPOINTERMODE	( 0 )

#if GETPOINTERMODE == 0
static void DeleteTargetStr( char* pDst, char* pSrc, char TargetStrCode )
{
	int Cnt = 0;

	for( int i = 0; i < strlen( pSrc ); i++ )
	{
		if( pSrc[ i ] != TargetStrCode )
			pDst[ Cnt++ ] = pSrc[ i ];
	}
}
#else
static void DeleteTargetStr( char** ppDst, char* pSrc, char TargetStrCode )
{
	int		Cnt				= 0;
	char	Temp[ 0x100 ]	= { 0, };

	for( int i = 0; i < strlen( pSrc ); i++ )
	{
		if( pSrc[ i ] != TargetStrCode )
			Temp[ Cnt++ ] = pSrc[ i ];
	}

	int		Length			= strlen( Temp );
	char*   pTemp			= new char[ Length + 1 ];
	strcpy( pTemp, Temp );
	pTemp[ Length ]			= 0;		// NULL文字忘れずに
	*ppDst					= pTemp;	// 最後にアドレスメモ
}
#endif

void main()
{
	char* str			= "abcxdxxefxf";

#if GETPOINTERMODE == 0
	char  Dst[ 0x100 ]	= { 0, };
	DeleteTargetStr( Dst, str, 'x' );
	printf( "%s\n", Dst );
#else
	char* pDst;
	DeleteTargetStr( &pDst, str, 'x' );
	printf( "%s\n", pDst );
#endif
// ポインタ取得の場合は解放をお忘れなく
#if GETPOINTERMODE == 1
	delete[] pDst;
#endif
}


指定した文字を消す関数を作って実装してみた形です。
基本的には適当なサイズなバッファでいいと思いますが、どうしても
きっちりと文字列数分のサイズしかとりたくない場合はdefineを1にした時のポインタバージョンを。
適当に書いただけなので、あまり参考にはならないかも・・

フリオ

Re: 文字型配列の指定文字の削除について

#5

投稿記事 by フリオ » 14年前

 一文字ずつコピーしながら対象文字を詰めていきます。

コード:

#include <stdio.h>

char *func(char *s, char c)
{
	char *p = s, *q = s;
	
	while(*p = *q ++) p += (*p != c);
	return s;
}

int main(void)
{
	char s[] = "abcxdxxexf";
	
	puts(s);
	puts(func(s, 'x'));
	return 0;
}

フリオ

Re: 文字型配列の指定文字の削除について

#6

投稿記事 by フリオ » 14年前

 結果を別の配列に入れる場合も同じ方法でできます。

コード:

#include <stdio.h>

char *func(char *s1, const char *s2, char c)
{
	char *p = s1;
	
	while(*p = *s2 ++) p += (*p != c);
	return s1;
}

int main(void)
{
	char s1[32], s2[] = "abcxdxxexf";
	
	puts(s2);
	puts(func(s1, s2, 'x'));
	return 0;
}

アバター
GRAM
記事: 164
登録日時: 14年前
住所: 大阪

Re: 文字型配列の指定文字の削除について

#7

投稿記事 by GRAM » 14年前

ちなみにですが、よりコンパクトにまとめることができます
実はcounterはなくなっても問題ありません、numXはその位置での個数を格納しているからです
よって二つのwhileは一つに集約されます
memsetもつかわないためもはやstring.hも必要ありません

コードはこうなります。(直観性は幾分失われる気がしますが)

コード:

#include <stdio.h>
 
char str[] = "abcxdxxexfgxxx";
int main(){
        int i = 0, numX = 0;
        do{
                if(str[i] == 'x'){
                        ++numX;
                        continue;
                }
                str[i-numX] = str[i];
        }while(str[i++] != '\0');
        printf(str);
        return 0;
} 

フリオ

Re: 文字型配列の指定文字の削除について

#8

投稿記事 by フリオ » 14年前

 最初の質問にかかれている方法も同じようにできます。

コード:

#include <stdio.h>

char *func(char *s, int c)
{
	char *p;
	size_t n = 0;
	
	for(p = s; *(p - n) = *p; ++ p) n += (*p == c);
	return s;
}

int main(void)
{
	char s[] = "abcxdxxexf";
	
	puts(s);
	puts(func(s, 'x'));
	return 0;
}

kaori

Re: 文字型配列の指定文字の削除について

#9

投稿記事 by kaori » 14年前

みなさん本当にありがとうございます^^
ちなみに新しく代入する配列を用意しないで同じ処理をすることはできませんか?
関数なら sakujo(char * str, char sakujo_moji)で受け取った文字型配列自体に削除文字を消した文字列を代入したいです。

閉鎖

“C言語何でも質問掲示板” へ戻る