C#reading .text文件格式化

时间:2013-04-24 15:32:07

标签: c# file-io

我有一个特定的文本文件:

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

5 个答案:

答案 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));