4

特定のテキストファイルがあります:

197 17.16391215
198 17.33448519
199 17.52637986
200 17.71827453
201 17.9101692
202 18.10206387
203 18.29395854
204 18.48585321
205 18.67774788
206 18.86964255
207 19.06153722

等々。明確にするために: 最初の列 (197,198,..) はフレーム番号を示し、2 番目の列 (17.xxx,...) はフレーム番号にリンクされた位置を示します。
ここで、各行を異なる配列に分けたいと思います。と

string[] delimiters = new string[] {" ", "\r\n" };
string line = reader.ReadToEnd();
string[] test = line.Split(delimiters, StringSplitOptions.None);

Textfile からのすべてのエントリを含む 1 つの配列を取得しました。しかし、すべてのフレーム番号 (最初の列) を 1 つの配列に、別の 2 番目の配列にすべての位置が必要です。

私がしなければならない仕事は次のとおりです: 位置番号 (例: 18.10) を取得し、2 番目の列の .txt ファイルで最も近い一致する番号を検索し、リンクされたフレーム番号 (この場合は 202) を返す必要があります。 . 私のアイデアは、2 つの一致する配列を生成し、1 つを検索して位置を検索し、もう 1 つからフレーム番号を返すことでした。インターネットで半日検索したところ、すべての .select のようなものがたくさん見つかりましたが、直接一致するものは何もありませんでした。しかし、多分私は現時点ではばかげています。

助けてくれてありがとう。うまくいけば、私の英語が理解できるでしょう:P

4

5 に答える 5

5

コメントに続いて EDIT 2 で、1 秒間に 24 回検索を繰り返したいと考えています。

まず、フレームのストリームを再生しようとしている場合、リストを検索するのは間違ったアプローチです。正しいアプローチは質問の範囲を超えていますが、基本的には連続データの表示を抑制したいと考えています。

値が変更されず、ルックアップがランダムであり、シーケンシャルではないという前提で。このようなコードを試すことができます。

private readonly List<int> ids = new List<int>();
private readonly IList<double> values = new List<double>();

public void LoadData(string path)
{
    foreach (var line in File.ReadLines(path))
    {
        var pair = line.Split(' ');
        this.ids.Add(int.Parse(pair[0]));
        this.values.Add(double.Parse(pair[1]));
    }
}

public double Lookup(int id)
{
    return this.values[this.ids.FindIndex(i => i >= id)];
}

より高いパフォーマンスが必要な場合は、ここで特殊なバイナリ検索を使用できます。

読んだ後に編集し、できれば理解してください

フレームがIDの昇順であると仮定します。

double GetFrameValue(string path, int limit)
{
    string [] parts;
    foreach (var line in File.ReadLines(path))
    {
       parts = line.Split(' '); 
       var frameId = int.Parse[0];
       if (frameId >= limit)
       {
           break;
       }
    }

    return double.Parse(parts[1]);
}

これには、必要な範囲だけファイルを読み取り、すべてをメモリに保持しないという明確な利点があります。Collectionファイルをランダムなフレーム位置で繰り返し読み取る場合は、ファイルが非常に大きい場合を除き、比較パフォーマンスが高速ないくつかにすべてをロードすることをお勧めします。


どうですか、

IEnumerable<KeyValuePair<int, double>> ReadFrames(string path)
{
    foreach (var line in File.ReadLines(path))
    {
       var parts = line.Split(' '); 
       yield return new KeyValuePair<int, double>(
           int.Parse(parts[0]),
           double.Parse(parts[1]));
    }
}

var frames = new Dictionary<int, double>(ReadFrames("yourfile.txt"));

var frameIds = frames.Keys;
var values = frames.Values;

コメントで述べたように、

var frames = File.ReadLines("yourfile.txt")
    .Select(line => line.Split(' '))
    .ToDictionary(pair => int.Parse(pair[0]), pair => double.Parse(pair[1])); 

var frameIds = frames.Keys;
var values = frames.Values;

同様に機能するはずです。

于 2013-04-24T15:43:15.287 に答える
2

わかりました、それで...

Frame2 つのプロパティを持つというクラスを作成しました。

 Number
 Position

次に、一度に 1 行ずつファイルを読み取り、行Frameごとに新しいファイルを作成して、スペースで行を分割し、新しいファイルFrameIList. これを行う簡単なプログラムのコードを次に示します。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        //Class to represent each frame
        public class Frame
        {
            //constructor..
            public Frame(string number, string position)
            {
                Number = number;
                Position = position;
            }

            public string Number { get; set; }
            public string Position { get; set; }
        }

        static void Main(string[] args)
        {
            string path = "c:\\data.txt";
            IList<Frame> AllFrames = new List<Frame>();

            foreach (string line in File.ReadLines(path))
            {
                //split each line at the space
                string[] parts = line.Split(' '); 

                //Create a new Frame and add it to the list
                Frame newFrame = new Frame(parts[0], parts[1]);
                AllFrames.Add(newFrame);
            }
        }
    }
}
于 2013-04-24T15:47:39.717 に答える
1

これから始めます:

IEnumerable<KeyValuePair<int, double>> ReadFrames(string path)
{
    return File.ReadLines(path).Select(l => 
    { 
        var parts = l.Split(' ').Select(p => p.Trim());
        return new KeyValuePair<int, double>(
               int.Parse(parts.First()),
               double.Parse(parts.Skip(1).First()));
    });
}

フレームができたので、位置番号でフレームを調べてみましょう。

int GetFrameByPosition(IEnumerable<KeyValuePair<int,double>> frames, double position)
{
    return frames.SkipWhile(f => f.Value < position).First().Key;
}

ワンライナーであることに注意してください。次のように呼び出します。

int frameNumber = GetFrameByPosition(GetFrames("path"), 18.10D);

別の質問に答える必要がある場合、それもワンライナーになる可能性があります。たとえば、そのコードは入力よりも大きい最初のフレームを取得しますが、最も近いものを要求しました。これは、この前のフレームである可能性があります。次のように実行できます。

int GetNearestFrameByPosition(IEnumerable<KeyValuePair<int,double>> frames, double position)
{
    return frames.OrderBy(f => Math.Abs(position - f.Value)).First().Key;
}

もう 1 つの例は、再生の開始位置をシークするためにこれを使用していて、その最初のフレームから始まるすべてのフレームが本当に必要な場合です。簡単です:

IEnumerable<KeyValuePair<int,double>> SeekToFrameByPosition(IEnumerable<KeyValuePair<int,double>> frames, double position)
{
    return frames.SkipWhile(f => f.Value < frames.OrderBy(f => Math.Abs(position - f.Value)).First().Key);
}

まだワンライナーです。

ここでの唯一の弱点は、ファイルが毎回ディスクから読み取られるたびにファイルに戻ると、低速になることです。それはあなたが必要とするものかもしれませんが、それを行う必要がない場合は、次のようにすべてのフレームを前もってメモリにロードすることで、簡単に高速化できます。

var cachedFrames = ReadFrames("path").ToList();

次に、ReadFrames() 関数を再度呼び出す代わりに、その cachedFrames 変数をどこでも使用します。

最後に、カスタム クラスの作成を支持して KeyValuePair の使用を避ける考え方があります。そのクラスは次のようになります。

public class Frame
{
    public int index {get;set;}
    public double position {get;set;}
}

上記のどこでもそれを使用してくださいKeyValuePair<int,double>。また、これは十分に小さい (< 16 バイト) ため、クラスではなく構造体と見なすことができます。構造体を使用する場合は、それをimmutableにすることもお勧めします。これは、コンストラクターでメンバーを設定し、後でそれらを決して変更しないという派手な方法です。

public struct Frame
{
   public Frame(int index, double position)
   {
      this.index = index; 
      this.position = position;
   }

   public int index {get;private set;}
   public double position {get;private set;}
}
于 2013-04-24T16:03:13.870 に答える
0

次のように、ファイル内の情報と一致するクラスを作成できます。

class FrameInfo
{
   public int Frame{ get; private set; }
   public double Position { get; private set; }

    public FrameInfo(int frame, double position)
    {
        Frame = frame;
        Position = position;
    }
}

または単に KeyValuePair を使用します

次に、データを解析します。

var frameInfos = File.ReadLines("MyFile.txt").
    Select(line => line.Split(' ')).
    Select(arr => new FrameInfo(int.Parse(arr[0]), double.Parse(arr[1]))).
    ToArray();

特定のフレームを検索する

var myFrame = frameInfos.First(fi => fi.Frame == someNumber);

ただし、これは O(N) 操作ですが、Dictionary を使用するとパフォーマンスが向上します。

編集:特定の位置に最も近いフレームを探している場合、これはうまくいく可能性があります:

    public static T MinValue<T>(this IEnumerable<T> self, Func<T, double> sel)
    {
        double run = double.MaxValue;
        T res = default(T);
        foreach (var element in self)
        {
            var val = sel(element);
            if (val < run)
            {
                res = element;
                run = val;
            }
        }
        return res;
    }

のように呼ばれる

var closestFrame = frameInfos.MinValue(fi => Math.Abs(fi.Position - somePosition));
于 2013-04-24T15:49:40.583 に答える