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

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

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

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

Q&A

解決済

5回答

1019閲覧

c#での文字比較方法について

kagura_63

総合スコア1

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

1グッド

2クリップ

投稿2024/07/01 09:26

編集2024/07/02 11:26

実現したいこと

下記2点をc#で実現し、最終的に例のような結果を取得したいです。
初学者のため足りない要素などございましたらご教示頂きたいです。

①文字列の比較(不一致箇所が先頭から何桁目にあるかを全ての文字で比較、大文字小文字不問)
②上記で抽出した不一致箇所が含まれる項目の項目名と値を抽出

考慮事項
・「項目」の個数が多い(100個以上)
・項目間での範囲重複がある
・文字列AとBのlengthが長い(500文字前後)

例:)
前提
比較文字A:12345678abcdefghijklmn
比較文字B:22345678abcdefghijjlmn

項目A:1文字目から8文字
項目B:1文字目から2文字
項目C:9文字目から13桁

欲しい結果
項目A, 比較文字A:12345678, 比較文字B:22345678
項目B, 比較文字A:12, 比較文字B:22
項目C, 比較文字A:abcdefghijklmn, 比較文字B:abcdefghijjlmn

発生している問題・分からないこと

実現方法が分かりません。
思いついたのは、項目○に対応する何桁目から何文字で区切った文字を配列に保持し、forで配列ごとに比較させる方法です。もっと簡単な実装方法があるはずなのでそれを知りたいです。
上記のコードや他に何かいい案があれば教えて頂きたいです。

該当のソースコード

C#

1まだ未実装です。

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

文字比較がt/fしか検索でヒットせず、該当するものが見つけられませんでした。

補足

Windows、VisualStudio2019を使用しています。

Nanashi508👍を押しています

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

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

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

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

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

YAmaGNZ

2024/07/01 09:54

比較文字列Aの1~8文字の"12345678"と比較文字列Bの1~8文字の”22345678”が異なった場合に項目1=Trueとする で合っていますか? 出されている例だと項目1、項目2、項目3全てTrueとなるであっていますか? もしそうなのであれば、 String.Substring メソッド https://learn.microsoft.com/ja-jp/dotnet/api/system.string.substring?view=netframework-4.8.1 を利用して各項目の部分を切り出して比較すればいいのではないですか?
kagura_63

2024/07/01 11:13

ご回答ありがとうございます。 比較して異なったらtrue、例では全てtrueとなる認識で相違ありません。 例で項目A〜Cを挙げましたが、切り出したい項目が100個以上の場合などはどのように比較するのが最適でしょうか? 全部の項目の開始桁数と文字数で区切るのはあまり美しくないので...案があればご教示頂きたいです🙇‍♂️
YAmaGNZ

2024/07/01 13:05

処理としては指定開始位置から必要文字数分切り出して比較すればいいだけですから関数化するのがいいかと思います。 bool CompareStr(String str1, String str2, int start, int length) { // 文字列1から必要分を切り出す(Substringを使用) // 文字列2から必要分を切り出す(Substringを使用) // 取り出した文字列を比較して戻り値として返す } といった感じで
winterboum

2024/07/01 13:13

欲しいのは「欲しい結果」すなわち②で、①は②のために必要な情報である と考えたので書いた という理解で合っていますか? つまり ②が必要。①はなくてもよい
kagura_63

2024/07/01 14:14

誤認識の通りです。 ①は②を出すための過程に必要だと感じたため記載しています。
winterboum

2024/07/01 22:52

そうかぁ〜〜 「誤」認識かぁ、、、、
kagura_63

2024/07/01 23:13

申し訳ありません。誤字です。 ご認識の通りです。
dodox86

2024/07/01 23:42

@質問者 kagura_63さん > 全部の項目の開始桁数と文字数で区切るのはあまり美しくないので...案があればご教示頂きたいです しばしば指摘されることですが、美しい/美しくない(≒醜い)とは個人の主観によるものも大きく、回答をいただいても結局質問者氏の気に入る、気に入らないで決められ、回答した方はガッカリすることも多いです。100個の項目があったとして結局はそれぞれの開始位置、文字数を各々意識する必要があり、実際に文字列を切り出すか1文字ずつ判定するしかないように思いますが、アルゴリズム、ロジックの問題でしょうか?それとも実装(実現するコード)の問題でしょうか。 もし実装の話だとして愚直にループで回すか、例えばLinqを使って少なくともコードの見た目がすっきりした感になるなどあります。実装の話だとしたら、ご自身でまず美しくない(と思う)実際に動くコードを提示しましょう。お話しはそこからになると思いますし、回答もよりいただき易くなると思います。
dodox86

2024/07/02 00:31

あれ、現時点で回答"2"件となっているのに表示されるのが1件だけですね。以前に別の質問で回答が消えてしまった、というようなコメントを見かけましたがteratailの不具合、あるいはバグでしょうか。
kagura_63

2024/07/02 03:25 編集

@dodox86 さん 美しなくないと表現したのはコードの見た目の話で、行数があまりにも多いのは読みづらいと感じるためそのような表現をしました。 現時点では100個の項目を切り出してループさせ比較する方法を考えていますが、c#に疎く実装まで辿り着けない現状です。 回答は私の方でも1名しか表示されていないため、不具合だと思われます。
fana

2024/07/02 06:21 編集

> 最適 なんだろう,そういう話はまず 考慮事項 を明言した方が良いのではないかと. ・「項目」の個数がとても多い(これについては「100個以上」と述べられてはいるが) ・項目間での範囲重複がとても多い ・文字列AとBの長さというのがとても長い 等など… 実際に想定される状況によって何が「最適」かというのは異なり得るのではなかろうか? (それとも「考え得る全ての場合に最適」とか言ってる感じ?) 実際に何をするための物なのか知りませんが,例えば項目間での「あり得る範囲の重なり方」みたいな話が何かあったりするならば(「あり得ない重なり方」の存在を考えなくて済むことによって)そのことに特化した方法が考えられる可能性とかがあるかもしれないし?
winterboum

2024/07/02 07:15

最適 ってどんな物差しで測るのかでかわりますから 課題にしにくいですね。 まず動くものを作ってみて、そのどこに不満があるか、それを治して改良版1を作る 改良版iのどこに不満が有るか、それを治して 改良版i+1をつくる と、最適版ではなく最良版を目指しては? 文字数最小 とか 行数最小 とか 関数の最大行数が最小とか じゃぁないですよね?
melian

2024/07/02 17:57 編集

> もっと簡単な実装方法があるはずなのでそれを知りたいです。 単純な実装ではありませんが、以下はLinq を利用する場合です。 https://dotnetfiddle.net/ryGK0U
guest

回答4

0

「項目」から文字列の範囲を表す数値を取得する手段としてRegexクラスを推奨します
以下はサンプルです

C#

1using System; 2using System.Linq; 3using System.Text.RegularExpressions; 4 5using static System.Console; 6 7public class EntryPoint 8{ 9 public static void Main(string[] args) 10 { 11 StringComparator strComparate = new("12345678abcdefghijklmn", "22345678abcdefghijjlmn"); 12 13 string[] titles = [ 14 "項目A:1文字目から8文字", 15 "項目B:1文字目から2文字", 16 "項目C:9文字目から13桁" 17 ]; 18 19 Sample(strComparate, titles); 20 } 21 22 public static void Sample(StringComparator strComparate, params string[] titles) 23 { 24 string[][] targets = new string[titles.Length][]; 25 26 for (int index = 0; index < titles.Length; ++index) 27 targets[index] = strComparate.Range(titles[index], "桁"); 28 29 for (int index = 0; index < titles.Length; ++index) 30 if (targets[index][0] != targets[index][1]) 31 WriteLine($"{titles[index][0..3]}:比較文字A:{targets[index][0]},比較文字B:{targets[index][1]}"); 32 } 33} 34 35public struct StringComparator(string sampleA, string sampleB) 36{ 37 int[] PutInt(string word) => ( 38 from numbers in Regex.Split(word, "[^0-9]") 39 where numbers != "" 40 select int.Parse(numbers) 41 ).ToArray(); 42 43 public string[] Range(string word, string? flag = null) 44 { 45 int[] numbers = PutInt(word); 46 47 return numbers.Length switch 48 { 49 2 => Range(numbers.First(), flag != null ? 50 (word.Contains(flag) ? numbers.Last() + numbers.First() : numbers.Last()) 51 : numbers.Last()), 52 53 < 2 => throw new FormatException("文字列には半角数値を二つ含めて下さい"), 54 > 2 => throw new FormatException("文字列に二つ以上の半角数値が含まれています"), 55 }; 56 } 57 58 public string[] Range(int start, int end) 59 { 60 Range range = (start - 1)..end; 61 return [$"{sampleA[range]}", $"{sampleB[range]}"]; 62 } 63}
項目A:比較文字A:12345678,比較文字B:22345678 項目B:比較文字A:12,比較文字B:22 項目C:比較文字A:abcdefghijklmn,比較文字B:abcdefghijjlmn

Regexクラスは正規表現による文字列検索をサポートします

C#

1int[] PutInt(string word) => ( 2 from numbers in Regex.Split(word, "[^0-9]") 3 where numbers != "" 4 select int.Parse(numbers) 5 ).ToArray();

「項目」から得た整数を文字列から数値型に変換し、それを用いて対象の文字列より特定の範囲を指定します

C#

1public string[] Range(string word, string? flag = null) 2 { 3 int[] numbers = PutInt(word); 4 5 return numbers.Length switch 6 { 7 2 => Range(numbers.First(), flag != null ? 8 (word.Contains(flag) ? numbers.Last() + numbers.First() : numbers.Last()) 9 : numbers.Last()), 10 11 < 2 => throw new FormatException("文字列には半角数値を二つ含めて下さい"), 12 > 2 => throw new FormatException("文字列に二つ以上の半角数値が含まれています"), 13 }; 14 }

flagにはカウント範囲の変更条件となる特定の単語を登録します
今回の場合であればを指定してカウントの初期位置を変更します

C#

1for (int index = 0; index < titles.Length; ++index) 2 targets[index] = strComparate.Range(titles[index], "桁");

「項目」のフォーマットが統一されていれば、固定の範囲から繰り返し必要な要素を取り出せます

C#

1for (int index = 0; index < titles.Length; ++index) 2 if (targets[index][0] != targets[index][1]) 3 WriteLine($"{titles[index][0..3]}:比較文字A:{targets[index][0]},比較文字B:{targets[index][1]}");

この方法は整数が必要数含まれていれば、どのような文字列にも対応できる利点がありますが、文字列解析のためのオーバーヘッドが発生する可能性があります
またこのサンプルでは用意できる項目数がA~Zの範囲に限定されます
複数桁の番号を割り当てる際などは改修が必要です

投稿2024/07/02 20:36

編集2024/07/02 21:03
TokoToko123

総合スコア25

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

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

kagura_63

2024/07/11 03:31

ご回答いただきありがとうございます。 すでに他の方の回答で実装しましたが、後学のためにこちらの方法でも実装させていただきます🙇‍♂️
guest

0

質問文だけからだと、ものすごく効率的な方法というものはなさそうな気がします。
(比較する位置が偏っているとかなら別ですが、ここではそうでないと仮定する。)

となると、方針としては、

・文字の比較回数を可能な限り減らす(1回だけにする)

ぐらいしかなさそうです。

もう少し具体的に考えると、

1.あらかじめすべての文字を比較して結果をbool型の配列に入れておく
2.項目ごとに、1で作成した配列を参照して「すべてがtrueではない」場合、項目や比較結果を記憶する

ということになるでしょう。

以上の考え方でコードを書くと以下のようになります。
(比較する文字列の長さが違う場合など、細かいところは考慮していないので参考まで。)

class Program { static void Main(string[] args) { // 比較する文字列 string s1 = "12345678abcdefghijklmn"; string s2 = "22345678abcdefghijjlmn"; // 項目 // (項目名, 開始位置, 長さ)の配列 (string, int, int) [] koumoku = [ ("A", 1, 8), ("B", 1, 2), ("C", 9, 14), ]; // あらかじめ全ての文字を比較して記憶しておく。 bool [] compare_results = new bool[s1.Length]; // 比較結果を入れておく配列 for (int i = 0; i < s1.Length; i++) { compare_results[i] = s1[i] == s2[i]; } // 結果のリストを宣言しておく。 // (項目名, 比較文字列A,比較文字列B)のリスト List<(string, string, string)> result = new List<(string, string, string)>(); // 項目ごとにチェックする。 foreach (var (name, index, len) in koumoku) { var start = index - 1; var end = start + len - 1; var sub = compare_results[start..end]; // 部分配列を取り出す。 if (!sub.All(x => x)) { // 「部分配列の値すべてがtrue」でないならば、 result.Add((name, s1.Substring(start, len), s2.Substring(start, len))); // 結果のリストに記録する。 } } // 結果出力 foreach(var (name, strA, strB) in result) { Console.WriteLine($"項目名: {name}, 比較文字列A: {strA}, 比較文字列B: {strB}"); } } }

投稿2024/07/09 05:09

JunSuzukiJapan

総合スコア314

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

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

kagura_63

2024/07/11 03:31

ご回答いただきありがとうございます。
guest

0

ベストアンサー

C#

1void Test() 2{ 3 string str1 = "12345678abcdefghijklmn"; 4 string str2 = "12335678Abcdefghijjlmn"; 5 (string Name, int start, int length)[] targets = { ("項目A", 1, 8), ("項目B", 1, 2), ("項目C", 9, 13), ("項目D", 9, 5) }; 6 7 foreach ((string Name, int start, int length) target in targets) { 8 var result = CompareStr(str1,str2,target.start,target.length); 9 Console.WriteLine($"{target.Name} : 結果 = {result.result} 比較文字A = {result.compStr_A}, 比較文字B = {result.compStr_B}"); 10 } 11} 12 13(bool result,string compStr_A,string compStr_B) CompareStr(string str1,string str2,int start,int length) 14{ 15 // 比較文字A、比較文字BはSubstringで指定されたstart(Substringは0始まり)からlength分切り出して比較する 16 // 大文字小文字を区別しないならString.Equalsを利用すればいいかと思います 17 // 戻り値をValueTupleとしているので、比較結果、比較文字A、比較文字Bを返す形になります 18} 19

といった感じにすればいいのではないでしょうか。
ValueTupleを利用して書いたのでそのあたりが分からなかったら調べてください。

投稿2024/07/02 06:54

YAmaGNZ

総合スコア10383

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

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

kagura_63

2024/07/11 03:29

ご回答いただきありがとうございます。 今までタプルに触ったことがあまりなかったのですが、1番実装イメージに近かったためBAとさせていただきました。
guest

0

c#は不得意なのですが「実現方法が分かりません。」ということなのでアルゴリズムを。
ぱっと見2つアプローチがあります。

共通

  1. 「切り出したい項目が100個」の登録。

1-1案 「項目名, 開始位置, 長さ」という構造体の配列を作る
1-2案 「項目名, 開始位置, 終了位置」という構造体の配列を作る
どちらが良いか、は比較方法で決まる

案A
A-1 ①を実装し、AとBが異なる文字であったらその場所を 配列 ここが違う! に入れる。
A-2 ここが違う! から一つづつ取り出し、構造体を評価し、含まれるかみる。
A-2' 構造体をひとつづつ取り出し、ここが違う!の中に該当するものがあるかみる
A-2 だと同じ構造体に複数回hitすることが有るので、その判定が増えるから、、
案B
B-1 構造体を一つづつ取り出し、
B-2 YAmaGNZさんの提案の方法
B-2' 部分文字列を切り出さなくても、A,Bそのままで比較は可能。
ただ、hitした時に部分文字列切り出し作業が発生するのがやかな。
めったにhitしないのか、めっちゃhitするのか、で判断

投稿2024/07/01 22:54

winterboum

総合スコア23487

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

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

fana

2024/07/02 05:58 編集

(正直質問文の日本語が難読すぎてイマイチ把握できてないのですが) 案A というのは以下のような話でしょうか? 長さが等しい文字列 A と B に関して,先頭から各文字が同一か否かを見ていく.文字が異なる位置 i が見つかる度に,構造体群の中で位置 i を範囲として含む物を探す. このとき,位置 i を含む構造体は別の「不一致だよリスト」に移動させる(以降は見ない)とかすれば A-2 の「複数回hit」を避けられるように思うのですがどうなんでしょう? (各範囲内での「不一致な文字の場所や個数」みたいな情報までは求められていないように見えるので) (同様に,範囲が走査され終えたやつらを「一致だよリスト」に移動させることもできる)
fana

2024/07/02 06:03

構造体の並び順か何かをうまいことしておけば,AとBの文字の比較箇所を限定できそうに思う. (どの構造体にも含まれていない範囲の箇所を見るのは明らかに無駄なので,構造体のデータから次の見るべき箇所を決めていくことを考えるべきなのだろう,と思う.)
winterboum

2024/07/02 06:16

>以下のような話でしょうか? 私の案そのものではないですが、これでも行けると思います
winterboum

2024/07/02 06:21

>(どの構造体にも含まれていない範囲の箇所を見るのは明らかに無駄なので,構造体のデータから次の見るべき箇所を決めていくことを考えるべきなのだろう,と思う.) これはアルゴリズムからだけでは決められない課題です。 どの構造体にも含まれていない範囲 が どのくらいあるのか、多ければ多いほど上の考えが意味を保ちます。ほとんどなければ調べる範囲を調整するというのが不要な負荷となります。 ただ、 その考え方で 調べる範囲を調整するのをどう行うのか、というのが厄介では? ん? もしかして 100を越える項目 というのは 常に固定?
fana

2024/07/02 10:02

> その考え方で 調べる範囲を調整するのをどう行うのか、というのが厄介では? 構造体を最初に開始位置でソートしておけば,それとなくやれそうな気も…? ま,とりあえずそういう話が本当に必要だということになったら(誰かが)考えるということで.
kagura_63

2024/07/02 11:32

@winterboumさん 項目の数、内容(開始位置や文字数)は常に不変です。 入ったな場所の位置や個数は出力を考えていません。
winterboum

2024/07/02 13:02

>項目の数、内容(開始位置や文字数)は常に不変 ということならば、それを解析してそれに基づくcodingするとより少ない実行時間で行えるようにできるかも、ですね。 ただし code は増えるかも。
kagura_63

2024/07/11 03:32

ご回答いただきありがとうございます。 すでに他の方の回答で実装しましたが、後学のためにこちらの方法でも実装させていただきます🙇‍♂️
winterboum

2024/07/11 07:56

JunSuzukiJapan さん的な手法で 2.項目ごとに、1で作成した配列を参照して「すべてがtrueではない」場合、項目や比較結果を記憶する をめっちゃ高速に行う方法も合ったりしますんで、色々な方法で速度試したい というようなことがあったら声を欠けてください
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.40%

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

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

質問する

関連した質問