質問するログイン新規登録
.NET

.NETとは、主に.NET Frameworkと呼ばれるアプリケーションまたは開発環境を指します。CLR(共通言語ランタイム)を搭載し、入力された言語をCIL(共通中間言語)に変換・実行することが可能です。そのため、C#やPythonなど複数の言語を用いることができます。

C#

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

Q&A

解決済

1回答

824閲覧

C#で行・列インデックスから文字列内のインデックスへの高速変換

Rei_312

総合スコア24

.NET

.NETとは、主に.NET Frameworkと呼ばれるアプリケーションまたは開発環境を指します。CLR(共通言語ランタイム)を搭載し、入力された言語をCIL(共通中間言語)に変換・実行することが可能です。そのため、C#やPythonなど複数の言語を用いることができます。

C#

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

0グッド

2クリップ

投稿2022/09/19 08:28

0

2

こんにちは。
文字列内で行・列を表すインデックスを文字列の先頭を0として始まるインデックスへ高速に変換したいのですが、自分ではうまく実装できないので、 より良い方法を教えていただきたいです。
インデックスが大きくなっても処理速度が極端に遅くなったりすることがないような実装はありますか?

IndexOfを使って実装したコード(インデックスが大きくなると遅くなる):

C#

1int GetCharIndex( string text, int line, int col ) 2{ 3 string code = "\r\n"; 4 int inLine = line; 5 6 int begin = 0; 7 for (int i = 0; true; i++ ) 8 { 9 if (line == 0) 10 { 11 if (inLine != 0) 12 begin += code.Length - 1; 13 break; 14 } 15 begin = text.IndexOf(code, begin); 16 if (begin == -1) 17 { 18 return -1; 19 } 20 line--; 21 begin++; 22 } 23 24 return begin + col; 25}

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

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

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

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

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

Zuishin

2022/09/19 10:24

実装できなかったコードだけ乗せられても。 それで説明は終わりですか?
Rei_312

2022/09/19 11:45

自分の実装が低速なので改善点を質問しています。 説明が足りないのなら何が足りないのか明示していただかないとわからないのですが。
odataiki

2022/09/19 12:42

質問文の内容が理解できませんでした。 ・具体的な引数データを示してみる  text="aaaa\r\nbbbb\r\ncccc\r\n....." line=1 col=1 ・「高速に変換」とありますが、  提示した引数の場合に何ミリ秒以内で結果が返ってくることを期待しているのか など質問者さんの期待する答えをもう少し具体的に提示すると回答が集まりやすくなるかと思いました。
Zuishin

2022/09/19 13:51

改良すべき点はありますが、線形探索なので、少々いじってもそこまで劇的に速くはならないでしょう。 バグがあるかどうかまでは見ていません。 テキストが固定ならあらかじめ各行の先頭をインデックス付けするなど検索しやすい前準備をしておくのが良いかもしれません。
fana

2022/09/22 02:00 編集

そもそもこれ,どう使う想定なんだろう? 私は頭悪いので,text の中身が完全に不明な状態下においてこれを真っ当に使用できる状況が想像つかない. 例えば, line 行目の文字列の長さよりも col の値が大きいかもしれないわけで,(しかもそうだったということは結果からは全くわからないのだけど)得られた値をまともに使えるのだろうか? (実際には return の直前あたりで col の妥当性をチェックする処理が入る?)
guest

回答1

0

ベストアンサー

更新 1万字超えたため元回答と置き換えました

わかりました。StringComparisonの問題でした(基本のはずなのですが失念しておりました^^;
StringComparison 列挙型 (System) | Microsoft Learn
.NET での文字列の比較に関するベスト プラクティス | Microsoft Learn

StringComparison.Ordinalならば、今より100~1000倍は速くなりました。
それでもReadOnlySpanのほうが速いことは確かなので、ROSを使えるなら使ったほうがいいでしょう^^

MethodNMeanErrorStdDevRatio
Original100693.671 us13.8602 us12.2867 us1.000
OriginalOrdinal1006.695 us0.0434 us0.0385 us0.010
ROS1005.053 us0.0245 us0.0217 us0.007
Original50020,997.678 us440.4404 us1,298.6484 us1.000
OriginalOrdinal50047.985 us0.9310 us0.8253 us0.002
ROS50039.873 us0.2115 us0.1979 us0.002
Original1000139,483.870 us1,282.5145 us1,136.9157 us1.000
OriginalOrdinal1000131.865 us1.2881 us1.0757 us0.001
ROS1000117.974 us2.3328 us2.3957 us0.001

cs

1using BenchmarkDotNet.Attributes; 2using BenchmarkDotNet.Running; 3 4 5BenchmarkRunner.Run(typeof(Program).Assembly); 6 7 8[ReturnValueValidator(true)] 9public class CharIndex 10{ 11 [Params(100, 500, 1000)] 12 public int N; 13 private string data; 14 15 [GlobalSetup] 16 public void GlobalSetup() 17 => data = string.Join("\r\n", Enumerable.Range(0, N).Select(x => new string('a', x))); 18 19 [Benchmark(Baseline = true)] 20 public int Original() => GetCharIndex(data, N - 1, N - 1); 21 22 [Benchmark] 23 public int OriginalOrdinal() => GetCharIndex2(data, N - 1, N - 1); 24 25 [Benchmark] 26 public int ROS() => GetCharIndex3(data, N - 1, N - 1); 27 28 int GetCharIndex(string text, int line, int col) 29 { 30 string code = "\r\n"; 31 int inLine = line; 32 33 int begin = 0; 34 for (int i = 0; true; i++) 35 { 36 if (line == 0) 37 { 38 if (inLine != 0) 39 begin += code.Length - 1; 40 break; 41 } 42 begin = text.IndexOf(code, begin); 43 if (begin == -1) 44 { 45 return -1; 46 } 47 line--; 48 begin++; 49 } 50 51 return begin + col; 52 } 53 54 int GetCharIndex2(string text, int line, int col) 55 { 56 string code = "\r\n"; 57 int inLine = line; 58 59 int begin = 0; 60 for (int i = 0; true; i++) 61 { 62 if (line == 0) 63 { 64 if (inLine != 0) 65 begin += code.Length - 1; 66 break; 67 } 68 begin = text.IndexOf(code, begin, StringComparison.Ordinal); 69 if (begin == -1) 70 { 71 return -1; 72 } 73 line--; 74 begin++; 75 } 76 77 return begin + col; 78 } 79 80 int GetCharIndex3(ReadOnlySpan<char> text, int line, int col) 81 { 82 var code = "\r\n".AsSpan(); 83 var inLine = line; 84 85 var begin = 0; 86 while (true) 87 { 88 if (line == 0) 89 { 90 if (inLine != 0) begin += code.Length - 1; 91 break; 92 } 93 94 begin = text.IndexOf(code, begin); 95 if (begin == -1) return -1; 96 97 line--; 98 begin++; 99 } 100 101 return begin + col; 102 } 103} 104 105static class Ex 106{ 107 // [Add Span / ReadOnlySpan IndexOf extensions taking in a start index and a count · Issue #26982 · dotnet/runtime](https://github.com/dotnet/runtime/issues/26982) 108 public static int IndexOf(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, int startIndex) 109 { 110 var indexInSlice = span.Slice(startIndex).IndexOf(value); 111 return indexInSlice == -1 ? -1 : startIndex + indexInSlice; 112 } 113}
// * Summary * BenchmarkDotNet=v0.13.2, OS=Windows 10 (10.0.19043.2006/21H1/May2021Update) Intel Core i7 CPU 920 2.67GHz (Nehalem), 1 CPU, 8 logical and 4 physical cores .NET SDK=7.0.100-rc.1.22431.12 [Host] : .NET 6.0.9 (6.0.922.41905), X64 RyuJIT SSE4.2 DefaultJob : .NET 6.0.9 (6.0.922.41905), X64 RyuJIT SSE4.2 | Method | N | Mean | Error | StdDev | Ratio | |---------------- |----- |---------------:|--------------:|--------------:|------:| | Original | 100 | 693.671 us | 13.8602 us | 12.2867 us | 1.000 | | OriginalOrdinal | 100 | 6.695 us | 0.0434 us | 0.0385 us | 0.010 | | ROS | 100 | 5.053 us | 0.0245 us | 0.0217 us | 0.007 | | | | | | | | | Original | 500 | 20,997.678 us | 440.4404 us | 1,298.6484 us | 1.000 | | OriginalOrdinal | 500 | 47.985 us | 0.9310 us | 0.8253 us | 0.002 | | ROS | 500 | 39.873 us | 0.2115 us | 0.1979 us | 0.002 | | | | | | | | | Original | 1000 | 139,483.870 us | 1,282.5145 us | 1,136.9157 us | 1.000 | | OriginalOrdinal | 1000 | 131.865 us | 1.2881 us | 1.0757 us | 0.001 | | ROS | 1000 | 117.974 us | 2.3328 us | 2.3957 us | 0.001 | // * Warnings * MultimodalDistribution CharIndex.Original: Default -> It seems that the distribution is bimodal (mValue = 3.4) // * Hints * Outliers CharIndex.Original: Default -> 1 outlier was removed (750.32 us) CharIndex.OriginalOrdinal: Default -> 1 outlier was removed (7.19 us) CharIndex.ROS: Default -> 1 outlier was removed (5.33 us) CharIndex.OriginalOrdinal: Default -> 1 outlier was removed (51.15 us) CharIndex.Original: Default -> 1 outlier was removed (143.96 ms) CharIndex.OriginalOrdinal: Default -> 2 outliers were removed (137.41 us, 139.30 us) CharIndex.ROS: Default -> 1 outlier was removed (127.26 us)

インデックスが大きくなっても処理速度が極端に遅くなったりすることがないような実装はありますか?

想像よりもかなり遅いですね。。。(1万行にしたら全然返ってこなくなりました^^;

行数もわからずノーヒントだったら、頭から探していくしかないような。

Replace("\r\n", "\n")して行数を求めて後ろ側だったら、LastIndexOfするとか?
置換や後ろから探すオーバーヘッドをペイできるのかどうか...
Charになるのはメリットかも?(IndexOf(String, Int32)よりは、IndexOf(Char, Int32)のほうが速そうな気はする)

投稿2022/09/19 14:30

編集2022/09/22 14:19
TN8001

総合スコア10140

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

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

Zuishin

2022/09/20 04:03

string.IndexOf は内部で Span<T> を使ってるだろうから速くなるはずがないと高をくくっていましたが、この結果はすごいですね。 なぜ数百分の一になるのか信じ難いです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.30%

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

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

質問する

関連した質問