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

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

新規登録して質問してみよう
ただいま回答率
85.46%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

3回答

16980閲覧

文字列から、ある文字と文字の間にある文字列をすべて抜き出したい

tento65

総合スコア11

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2017/06/02 05:16

###前提・実現したいこと
C言語で、文字列からある文字と文字の間にあるものをすべて抜き出したい。

例えば下のように<a><b>のタグがあり、その間の文字をすべて出力をしたいです。

<a> <b>
例1:<a><a>vvv<b><a>vvv

例2:<a>ai<b><a>aaa<b>→ai
aaa

###該当のソースコード

#include <stdio.h> #include <stdlib.h> #include <string.h> int main(void) { //開始タグ、終了タグを入力 char tag[101]; fgets(tag, sizeof(tag), stdin); char *start, *end; start = strtok(tag, " "); end = strtok(NULL, "\n"); //文字列を入力 char tmp[5001]; fgets(tmp, sizeof(tmp), stdin); //アウトプット用 char memory; char output[5001]; int wordcnt = 0; //検索用 char *s; char *e; //囲んだ文字が見つかるまでループ while (1) { //開始タグで検索 if (( s = strstr(tmp,start) ) != NULL) { int searchS = atoi(s+strlen(start)); //見つかったら終了タグで検索 if ((e = strstr(tmp, end)) != NULL) { int searchE = atoi(e); //文字列の抜き出し strncpy(&memory, tmp+strlen(start), searchE-searchS); //文字列の配列に記録 output[wordcnt] = memory; //どちらか見つからなければ終了 }else break; }else break; wordcnt++; } //ループ終了 for (int i = 0; i < wordcnt; i++) printf("%s\n",output[i]); return 0; }

###試したこと
strncpyで文字列の抜き出しのみができるということで、文字列検索のstrstrを併用しました。

strstrの戻り値で、始まりの文字列と終わりの文字列を検索して、あれば2つの値を使い、strncpyで抜き出して全てを出力し、無ければループを抜けるという流れの予定なのですが、配列の数の分(5001回?)がnullで出力されてしまいます。
ループを抜けたいですが、検索できなくなったら抜け出すという方法が思いつきませんでした。

また、strncpyで入れた記憶用のmemoryという変数を出力した結果、これもnullでした。
他の変数については、

searchE、searchSはすべて0

tmpはテスト毎に数値は別

となっていました。

直すとすればどこでしょうか?そもそものやり方が間違っているかもわかりません。

###補足情報(言語/FW/ツール等のバージョンなど)
VisualStudio2015
win32 C++

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

C言語では、メモリの確保・解放のタイミングが常に問題になりますが、そのあたりは調整して下さい。
テストをしやすくするために、必要な文字列は全てコマンドライン引数から取得するようにしています。

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4 5 6/* 7 * 文字列stringの中から、文字列startと文字列endに挟まれる文字列を抽出して文字列配列extractionsに返し、 8 * その個数を戻り値として返す。 9 * ただし、extractionsにはあらかじめ十分な配列要素が確保されているものとし、 10 * 抽出文字列用バッファはextract_between自身が確保するものとする。 11 * 12 */ 13int extract_between(char *string, char *start, char *end, char **extractions); 14 15 16int main(int argc, char **argv) 17{ 18 int extraction_count; 19 char **extractions; 20 int i; 21 22 if (argc <= 3) { 23 fprintf(stderr, "Usage: %s string start end\n", argv[0]); 24 return 1; 25 } 26 27 extractions = (char **)malloc(sizeof(char *) * strlen(argv[1])); 28 29 extraction_count = extract_between(argv[1], argv[2], argv[3], extractions); 30 31 for (i = 0; i < extraction_count; i++) { 32 printf("%d\t%s\n", i + 1, extractions[i]); 33 } 34 35 for (i = 0; i < extraction_count; i++) { 36 free(extractions[i]); 37 } 38 39 free(extractions); 40 41 return 0; 42} 43 44 45int extract_between(char *string, char *start, char *end, char **extractions) 46{ 47 int count = 0; 48 char *scanner; 49 char *found_start_at; /* 文字列stringで文字列startが見つかった最初の位置 */ 50 char *found_end_at; /* 文字列stringで文字列endが見つかった最初の位置 */ 51 int length_of_start; 52 int length_of_end; 53 54 length_of_start = strlen(start); 55 length_of_end = strlen(end); 56 57 for (scanner = string; 58 (found_start_at = strstr(scanner, start)) != NULL && 59 (found_end_at = strstr(found_start_at + length_of_start, end)) != NULL; 60 scanner = found_end_at + length_of_end) 61 { 62 char *extraction; 63 int length_of_extraction = found_end_at - found_start_at - length_of_start; 64 65 extraction = (char *)malloc(length_of_extraction + 1); 66 strncpy(extraction, found_start_at + length_of_start, length_of_extraction); 67 extraction[length_of_extraction] = '\0'; /* strncpyは文字列にに'\0'を付加しないので、付加する。 */ 68 69 extractions[count++] = extraction; 70 } 71 72 return count; 73}

実行結果:

bash

1> ./extract_between '<a>ai<b><a>aaa<b>' '<a>' '<b>' 21 ai 32 aaa 4

投稿2017/06/02 11:23

naomi3

総合スコア1105

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

LOOPしてませんので参考まで

c

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4 5int main(void) { 6 //開始タグ、終了タグを入力 7 char tag[101]; 8 fgets(tag, sizeof(tag), stdin); 9 char *start, *end; 10 start = strtok(tag, " "); 11 end = strtok(NULL, "\n"); 12 13 //文字列を入力 14 char tmp[5001]; 15 fgets(tmp, sizeof(tmp), stdin); 16 17 //アウトプット用 18 char output[5001]={0}; 19 int wordcnt = 0; 20 21 //検索用 22 char *s; 23 char *e; 24 char *searchS, *searchE; 25 26 //開始タグで検索 27 if (( s = strstr(tmp,start) ) != NULL) { 28 29 searchS = (s+strlen(start)); 30 31 //見つかったら終了タグで検索 32 if ((e = strstr(tmp, end)) != NULL) { 33 34 searchE = e; 35 36 //文字列の抜き出し 37 if ( searchE > searchS ) 38 memcpy(output, searchS, searchE-searchS); 39 40 } 41 } 42 43 printf("%s\n",output); 44 45 return 0; 46} 47[242@ts21adm

投稿2017/06/02 07:18

編集2017/06/02 08:59
A.Ichi

総合スコア4070

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

  • ある任意長の文字列がある
  • この文字列の中から、<a><b> で囲まれた部分を抽出したい
  • ただし、<a><b>の組み合わせは複数回出てくることがありうる

ということですかね。

  • <a><a><b><b>(入れ子) のような状態は考慮しない

のであれば、手順は次のようになります。

  1. 着目位置(pi)を文字列の先頭([0])とする
  2. piから探して、最初に来る<a>の位置(pa)を探す
  3. pa(実際にはpa+3からでよい)から探して、最初に来る<b>の位置(pb)を探す
  4. (pa+3)から(pb-1)までを切り出し、結果の文字列に追加する
  5. piを pb+3 とする(<b>の次の位置へずらす)
  6. pi が文字列の末尾を超えない限り、2. からを繰り返す

ただし、<b> が足りない場合を考えると、このまま実装すると最悪例外で落ちます。
※pbを探したときに変なことになるため

まあそこは課題ということで。

投稿2017/06/02 05:59

tacsheaven

総合スコア13703

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問