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,..) 表示帧号,第二列 (17.xxx,...) 表示链接到帧号的位置,反之亦然。
现在我想将每一行分隔在不同的数组中。和

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

我得到了一个包含文本文件中所有条目的数组。但我想要一个数组中的所有帧号(第一列)和另一个第二个数组中的所有位置。

我要做的工作如下:我将获得一个位置编号(例如 18.10)然后我必须在第二列的 .txt 文件中搜索最接近的匹配编号并返回链接的帧编号(在本例中为 202) . 我的想法是生成两个匹配的数组,在一个中搜索位置并从另一个返回帧号。当我在互联网上搜索半天时,我发现了很多类似 .select 的东西,但没有任何直接匹配的东西能解决我的问题;但也许我现在很愚蠢。

所以感谢您的帮助。希望你能听懂我的英语:P

4

5 回答 5

5

编辑 2 在您的评论之后,您希望每秒重复搜索 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

好吧,那么...

我创建了一个名为 的类Frame,具有两个属性:

 Number
 Position

然后,我将读取文件,一次一行,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 回答
2

您可以使用 LINQ 来简化代码。我在这里假设位置是双数。

string filePath ="C:\wherethefileIs";
double targetPosition = 18.10;

var query = from line in File.ReadAllLines(filePath)
                    let dataLine = line.Split(new[] {' '})
                    select new
                        {
                            Frame = Int32.Parse(dataLine[0]),
                            Position = Double.Parse(dataLine[1])
                        };

var nearestFrame = query.OrderBy(e => Math.Abs(e.Position - targetPosition)).Select(e=>e.Frame).FirstOrDefault();
于 2013-04-24T15:53:54.890 回答
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;
}

另一个例子是,如果您使用它来寻找播放的起始位置,并且您确实希望所有帧都从第一帧开始。很简单:

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();

然后只需在任何地方使用该 cachedFrames 变量,而不是重新调用 ReadFrames() 函数。

最后,有一种思想流派会避免使用 KeyValuePair 来支持创建自定义类。该类可能如下所示:

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

在上面看到的任何地方都使用它KeyValuePair<int,double>。此外,它足够小(< 16 字节),您可以考虑使用结构而不是类。如果你确实使用了一个结构,最好也让它成为不可变的,这是一种奇特的说法,你在构造函数中设置了成员,然后以后再也不改变它们:

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) 操作,字典会产生更好的性能。

编辑:如果您正在寻找最接近某个位置的框架,这可能有效:

    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 回答