お世話になります。最近文字列を勉強し始めたのですが、文字列になるとてんでダメになってしまいます・・・。お聞きしたいことは2つあります。
1.テキストファイルから英単語を読み込み、それらをアルファベット順に並べ、新たにテキストファイルに書き込むというものです。例えば、
january
February
march
April
Apple
と読み込んだなら、
Apple
April
February
january
march
と書き込みたいのです。文字列を配列に格納し、一文字一文字比較していきたいのですが、文字だとどのように比較して順番を変えればよいのでしょうか。数字の比較なら簡単に出来るんですが、文字だとわからなくなってしまいます。
2.これも同じく文字なのですが、テキストファイルから英単語、あるいは英語のフレーズを2つ読み込み、その2つの英語がアナグラムかどうかを判定するものです。
アナグラム→http://d.hatena.ne.jp/keyword/%A5%A2%A5ʥ%B0%A5%E9%A5%E0
例えば、「O, Draconian devil!」と「Leonardo da Vinci!」を読み込んでアナグラムか判定します。ちなみにこれはアナグラムです。これも一文字一文字比較していくと思うのですが、どのように比較すれればよいのかがわかりません。どなたか宜しくお願い致します。
No.2
- 回答日時:
文字列の比較の仕方ですね。
strcmp などの関数を使うと大小関係が得られます。
a<b<・・・<z です。アスキーコードだったら
a=31 b=32 となっています。
御自分で比較する場合は、文字として比較します。
str1[0] < str2[0]
といった具合ですが、文字列の数だけ行う必要が
あります。
No.3
- 回答日時:
> 「O, Draconian devil!」と「Leonardo da Vinci!」
双方の文字列をそれぞれ昇順または降順に並べ替えます。
並べ替えた文字列を、先頭から1文字ずつ比べていきます。
途中に食い違いがあればアナグラムではありません。
最後まで同じであればアナグラムです。
No.4
- 回答日時:
★1.は文字列群を昇順にソートし、ソート結果をファイル出力。
・ソートでは strcmp() を用いる(戻り値、正負で大小判定)。
----------------------------------------------
☆質問者様の2.における例文字列、
「O, Draconian devil!」と
「Leonardo da Vinci!」を見ると、
, が一方のみに使われ、文字列長が異なる。
(プロポーショナルでの見かけの文字列長調整?)。
ことから、2つの文字列内をソートし、結果どうしを「比較」する方法では難しいと思います。
★大文字・小文字を同じとして、26文字種の使用数をカウントし、
双方の使用数が同じかどうかで判定すれば、と思います。
a と A の使用数が iCnt[0] に入ります(◆)。
char cDevil[32], cVinci[32];
int iCnt[26] = { 0 }, iCode, i;
for( i = 0; i < 32; i++ ){ // cDevil
if( 0x00 == cDevil[i] ) break; // 文字列終わり
if( 0 == isalpha( cDevil[i] ) ) continue; // 「英文字」判定
iCode = toupper( cDevil[i] ) - 0x41; // 大文字化(◆)
iCnt[ iCode ]++; // 加算
}
for( i = 0; i < 32; i++ ){ // cVinci
if( 0x00 == cVinci[i] ) break;
if( 0 == isalpha( cVinci[i] ) ) continue;
iCode = toupper( cVinci[i] ) - 0x41;
iCnt[ iCode ]--; // 減算
}
for( i = 0; i < 26; i++ ){ // アナグラム判定
if( 0 == iCnt[i] ) continue;
printf( "アナグラムではありません\n" );
break;
}
注:インデントに全角空白を用いています。
参考URL:http://e-words.jp/p/r-ascii.html
この回答への補足
皆様ご回答本当にありがとうございます!
皆様のいうstrcmp関数を始めて知りました。全然勉強不足です。頑張ります!
さて、1つ目ですが、strcmpの使い方を調べ数時間粘ったのですが、躓いてしまいました。
strcmpを使う場合、2つの配列が必要となりますよね。
しかしこの場合、私は配列をs[256]と1つだけ用意し、テキストファイルから読み込んでいるのですが、これだと比較をどうすればいいのかがわかりません。
配列を2つ用意しようとすると、今度はどうテキストファイルから文字を読み込み2つの配列に格納すればいいのかがわからなく躓いてしまいました。
これを解決する方法はありますでしょうか?
No.5
- 回答日時:
No4 です。
>私は配列をs[256]と1つだけ用意し、
>テキストファイルから読み込んでいるのですが
テキストファイルが、
january
February
march
April
Apple
のようになっていると s[256] には最後の Apple だけが入り、
それ以前の行(レコード)のデータは上書きされてしまいます。
(まさか連結なんかしてないよね?←複雑そう)
★次のように、格納してからでないと・・・。
char cBuf[256], cStore[GYO][256];
while( NULL != fgets( cBuf, 256, fp ) ){
strcpy( cStore[iLine++], cBuf ); // 格納
}
・
(ソート)
if( 0 < strcmp( cStore[i], cStore[j] ) ){
(入れ替え(別の配列に待避などで))
}
(出力)
☆ソートについては、検索すればいっぱい出てきます。
--------------------------------------------------
★前回の回答で、関数化できる部分がありました。
・
Count26( cDevil, iCnt, 1 ); // 関数呼び出し
Count26( cVinci, iCnt, -1 );
・
}
void Count26( char cWork[], int iCnt[], int iAdd )
{
int i, iCode;
for( i = 0; i < 32; i++ ){
if( 0x00 == cWork[i] ) break; // 文字列終わり
if( 0 == isalpha( cWork[i] ) ) continue; // 「英文字」判定
iCode = toupper( cWork[i] ) - 0x41; // 大文字化にして
iCnt[ iCode ] += iAdd; // 加減
}
}
この回答への補足
ご返答ありがとうございます!
まず2つ目で質問です。
とりあえずキーボードから文字を入力し試しました。
printf("文字を入力\n");
scanf("%s", cDevil);
scanf("%s", cVinci);
そしてyama5140さんのおっしゃるようにプログラムを動かしてみたのですが、エラーが起きてしまいました。
「O, Draconian devil」のようにスペースを入れると、cDevilにO,が、cVinciにDraconianが入ってしまい、セグメンテーションエラーを起こしてしまいました。
私のこのやり方はまずかったでしょうか・・・?
2、スペースを入れなくても、どの文字を入力してもアナグラムではないと表示されてしまいます。
例えば単純に、erosとroseを入力してもアナグラムではないと表示されます。
3、関数ですが、矛盾と出てしまいました。
Count26( cDevil, iCnt, 1 );
Count26( cVinci, iCnt, -1 );
と、Count26が2つあるからでしょうか。
No.6
- 回答日時:
> 「O, Draconian devil」のようにスペースを入れると、cDevilにO,が、cVinciにDraconianが入ってしまい、セグメンテーションエラーを起こしてしまいました。
> 私のこのやり方はまずかったでしょうか・・・?
scnaf()を使うと、スペースを受け取った時点で、例えばcDevilへの
入力を終了したものとみなします。
別の関数、例えばfgets()あたりを使ってみてはいかがでしょうか。
> 例えば単純に、erosとroseを入力してもアナグラムではないと表示されます。
どういったコードでですか?
> と、Count26が2つあるからでしょうか。
何をしたときに矛盾と出たのでしょうか?
行なったこととエラーメッセージを正確に教えてください。
なお、どの関数を何回呼び出しても全く問題ありません。
この回答への補足
メンテ終わりましたね^^ 初めの質問の所で誤りがありました。
アナグラムかどうか判断するプログラムは、テキストファイルから読み込むのではなく、キーボードから打ち込みます。
それと、比較はcase-insensitiveなので大文字小文字は区別しません。すみませんでした。
1つ目のプログラムは出来ました。皆様ありがとうございました!
>asuncion様
キーボードからの入力でscanfを使用したのですが、ご指摘されたようにスペースを使うと正しく入力されないですね。
fgetsはファイルを読み込むための関数でしたよね。これは私の誤りでした。申し訳ありません。
スペースを使っても正しく入力できる関数はありますでしょうか。
eros、roseを試したコードはyama5140様のコードで試させて頂きました。
以下、yama5140様のコードです。
char cDevil[32], cVinci[32];
int iCnt[26] = { 0 }, iCode, i;
for( i = 0; i < 32; i++ ){ // cDevil
if( 0x00 == cDevil[i] ) break; // 文字列終わり
if( 0 == isalpha( cDevil[i] ) ) continue; // 「英文字」判定
iCode = toupper( cDevil[i] ) - 0x41; // 大文字化(◆)
iCnt[ iCode ]++; // 加算
}
for( i = 0; i < 32; i++ ){ // cVinci
if( 0x00 == cVinci[i] ) break;
if( 0 == isalpha( cVinci[i] ) ) continue;
iCode = toupper( cVinci[i] ) - 0x41;
iCnt[ iCode ]--; // 減算
}
for( i = 0; i < 26; i++ ){ // アナグラム判定
if( 0 == iCnt[i] ) continue;
printf( "アナグラムではありません\n" );
break;
}
asuncion様が以前ご指摘されたように、まずO, Draconian devil!を昇順に並べ替え、次にLeonardo da Vinci!を同じく昇順に並べ替えようとしたのですが、
この方法を調べても、2つ以上の文字列を昇順にする方法(私の1つ目の質問のような)は沢山見つかるのですが、1つの文字列を昇順にする方法が見つかりません。
良い方法はありますでしょうか。
No.7
- 回答日時:
> fgetsはファイルを読み込むための関数でしたよね
fgets()の第3引数にstdinを指定すれば、標準入力(例:キーボード)からの
入力を受け取れます。
No.9
- 回答日時:
No4, 5 です。
>1つ目のプログラムは出来ました。皆様ありがとうございました!
よかったですね、「ソート」は一度習得すれば、あとは応用ですから。
-------------------------------------------------
>No.3 様が以前ご指摘されたように、まずO, Draconian devil!を
>昇順に並べ替え、次にLeonardo da Vinci!を同じく昇順に並べ替
>えようとしたのですが・・(投稿者一部修正)
★No4 で記しましたように、この方法では難しいと思います。
>それと、比較はcase-insensitiveなので大文字小文字は区別しません。
★このことの処理を、「昇順に並べ替え」る前にしないといけませんね。
-------------------------------------------------
>1つの文字列を昇順にする方法が見つかりません。
★以降に示します(「丸投げ」返球ですが、この方法では本来の目的が叶えられないことを示すため、ソース全文を投稿します)。
#include <stdio.h>
#include <string.h>
void Sort32( char cWork[] )
{
int i, j, iLen;
char cDummy;
iLen = strlen( cWork );
for( i = 0; i < iLen; i++ ){
for( j = i; j < iLen; j++ ){
if( cWork[i] < cWork[j] ) continue;
cDummy = cWork[i];
cWork[i] = cWork[j];
cWork[j] = cDummy;
}
}
}
void main()
{
char cDevil[32] = "O, Draconian devil!";
char cVinci[32] = "Leonardo da Vinci!";
Sort32( cDevil );
Sort32( cVinci );
printf( "%s\n", cDevil );
printf( "%s\n", cVinci );
}
注:インデントに全角空白を用いています(タブに一括変換して下さい)。
☆実行すると、
「__!,DOaacdeiilnnorv」
「__!LVaacddeiinnoor」 となります。
(投稿表示のため、半角スペースの代わりに _ を使用)
No.3 さんの
>並べ替えた文字列を、先頭から1文字ずつ比べていきます。
★↑難しいどころか、できないですよね。
★「並べ替え」方式でなく、「26文字種の使用数をカウント」方式を
お勧めします( case-insensitive 対応)。
これですと、例のように文字列長が異なる場合にも使えます。
文字列をそれぞれ大文字に変換してソートし比較するようなプログラムを作りました。
しかしどちらか一方の文字列に!や,が含まれているとやはり比較してもアナグラムと判断できませんね・・・。
大文字変換の際、配列が!や,に当たると、その配列自体を削除してしまえば、と考えたのですが、その方法がわかりませんでした・・・。
yama5140様がご指摘したように、使用数をカウントする方法で考えたいと思います。本当にありがとうございました!
No.10ベストアンサー
- 回答日時:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int isanagram1(char *str1, char *str2)
{
int c1[256] = {0}, c2[256] = {0}, i;
while(*str1) c1[tolower(*str1 ++)] ++;
while(*str2) c2[tolower(*str2 ++)] ++;
for(i = 0; i < 256; i ++){
if(isalpha(i) && (c1[i] != c2[i])) return 0;
}
return 1;
}
void selectalpha(char *str)
{
char *c0, *c1;
for(c0 = c1 = str; *c0 = tolower(*c1); c1 ++) c0 += (isalpha(*c0) != 0);
}
void count(char *str, int *c)
{
char *al = "abcdefghijklmnopqrstuvwxyz", *p;
selectalpha(str);
while(*str){
if((p = strchr(al, *str ++)) != NULL) c[p - al] ++;
}
}
int isanagram2(char *str1, char *str2)
{
int c1[26] = {0}, c2[26] = {0}, i;
count(str1, c1);
count(str2, c2);
for(i = 0; i < 26; i ++){
if(c1[i] != c2[i]) return 0;
}
return 1;
}
int compc(const void *c1, const void *c2)
{
return *(char *)c1 - *(char *)c2;
}
int isanagram3(char *str1, char *str2)
{
selectalpha(str1);
selectalpha(str2);
qsort(str1, strlen(str1), sizeof(char), compc);
qsort(str2, strlen(str2), sizeof(char), compc);
return !strcmp(str1, str2);
}
int main(void)
{
char str1[32] = "O, Draconian devil!";
char str2[32] = "Leonardo da Vinci!";
char *result[2] = {"Not Anatram", "Anagram"};
puts(str1);
puts(str2);
printf("%s\n", result[isanagram1(str1, str2)]);
puts(str1);
puts(str2);
printf("%s\n", result[isanagram2(str1, str2)]);
puts(str1);
puts(str2);
printf("%s\n", result[isanagram3(str1, str2)]);
puts(str1);
puts(str2);
return 0;
}
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- その他(プログラミング・Web制作) 2つのテキストファイルを比べて文字列を特定する方法を教えて下さい 5 2022/05/01 15:22
- C言語・C++・C# C言語で 英文字のみからなる文字列、”Radar”、”WasItACatISaw”、”a”、””(空 5 2022/12/20 15:17
- Excel(エクセル) エクセルの日付から年末尾2桁と月を1月をA~Lに変換したい 3 2022/08/08 10:32
- Excel(エクセル) エクセルで2つの表を比較して、文字列が同じだが、その行のある値が違うものを抽出したい 1 2022/10/06 21:48
- Excel(エクセル) PowerQueryに詳しい方教えてください(Office365) 1 2022/07/24 21:11
- C言語・C++・C# [C言語] コメント文字列を無視して、数値データを読み込むプログラム部分について 5 2022/10/05 11:03
- C言語・C++・C# いまc言語の標準ライブラリ文字列を勉強しているのですがいまいちわかりません。 strcmpとmemc 5 2023/07/04 15:34
- Visual Basic(VBA) 違う文字のみ色を塗る方法がわかりません。。 G列とH列のに文字が入っています。 例えばG5とH5の文 3 2022/06/03 17:13
- Visual Basic(VBA) EXCEL VBA 単語置き換え について質問です ブック名 ぶぶぶ シート名 ししし セル V3〜 3 2023/03/08 01:41
- Visual Basic(VBA) 特定の文字を簡単な操作で半角スペースに変換するか削除したい 2 2022/11/01 10:35
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
C言語のfor文です。 繰り返しの...
-
strchr() の第2引数はなぜ int ...
-
CStringをwchar_tに変換したい
-
_TCHAR*での引数の読み込み
-
間接操作のレベルとは
-
fgetsなどのときのstdinのバッ...
-
c++ 文字列を入力して、一文字...
-
C言語の入力した文字を反転させ...
-
ftoa の作り方
-
標準ライブラリ関数の自作につ...
-
c言語です。
-
ネットワークにつながっている...
-
str系関数を使わずに二つの文字...
-
構造体のアライメント調整
-
間接参照のレベルが異なっています
-
文字列から空白を取り除きたい...
-
起動時の引数の取得方法が分か...
-
コマンドラインに入力されてい...
-
配列をnビットシフトする
-
RGB→YUV変換のプログラム
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
fgetsなどのときのstdinのバッ...
-
charでの計算?
-
C言語のfor文です。 繰り返しの...
-
charからLPTSTRへの変換方法
-
文字列から空白を取り除きたい...
-
C言語の入力した文字を反転させ...
-
'const char *' 型は 'char *' ...
-
配列をnビットシフトする
-
str系関数を使わずに二つの文字...
-
int main()の・・・
-
atoi( ) の反対をやりたい
-
CStringをwchar_tに変換したい
-
c++ 文字列を入力して、一文字...
-
switch文で文字を比較すること...
-
干支のプログラム
-
3桁区切(コンマ)記号をつけ...
-
絶対パスからのファイル名の切...
-
間接操作のレベルとは
-
間接参照のレベルが異なっています
-
型変換
おすすめ情報