我有一个特定的文本文件:
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,..)表示一个framenumber,第二列(17.xxx,...)表示链接到framenumber的位置,反之亦然。
现在我想在不同的数组中分隔每一行。随着
string[] delimiters = new string[] {" ", "\r\n" };
string line = reader.ReadToEnd();
string[] test = line.Split(delimiters, StringSplitOptions.None);
我有一个包含Textfile中所有条目的数组。但我希望所有framenumbers(第一列)在一个Array中,而在另一个第二个Array中所有位置。
我的工作我必须做的如下:我将获得一个位置编号(例如18.10)然后我必须在第二列的.txt文件中搜索最接近的匹配编号并返回链接的框架编号(在此案例202)。我的想法是生成两个匹配的数组,在一个中搜索位置,从另一个中返回framenumber。当我在互联网上搜索了半天时,发现很多东西,比如所有的.select东西,但没有任何东西直接匹配我的问题;但也许我此刻是愚蠢的。
非常感谢您的帮助。希望你能理解我的英语:P
答案 0 :(得分: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)];
}
如果需要更高的性能,您可以使用专门的二进制搜索here。
阅读后编辑,希望了解
并假设帧按递增的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;
应该也可以。
答案 1 :(得分:2)
好的,所以......
我创建了一个名为Frame
的类,它有两个属性:
Number
Position
然后,我会一次一行地读取该文件,并在每行创建一个新的Frame
,在该空格处拆分该行并将新的Frame
添加到{{ 1}}。这是一个简单程序的代码:
IList
答案 2 :(得分: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();
答案 3 :(得分: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字节),您可以考虑一个结构,而不是一个类。如果你确实使用了一个结构,那么也可以使它成为 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;}
}
答案 4 :(得分: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));