C# 从文本文件中读取双值

C# 从文本文件中读取双值,c#,C#,尝试使用C#应用程序从文本文件读取数据。有多行数据,每行数据都以一个整数开始,然后是一堆双精度值。文本文件的一部分如下所示 33 0.573140941467E-01 0.112914262390E-03 0.255553577735E-02 0.497192659486E-04 0.141869181079E-01-0.147813598922E-03 34 0.570076593453E-01 0.100112550891E-03 0.256427138318E-02-0.868691490

尝试使用C#应用程序从文本文件读取数据。有多行数据,每行数据都以一个整数开始,然后是一堆双精度值。文本文件的一部分如下所示

33 0.573140941467E-01 0.112914262390E-03 0.255553577735E-02 0.497192659486E-04 0.141869181079E-01-0.147813598922E-03
34 0.570076593453E-01 0.100112550891E-03 0.256427138318E-02-0.868691490164E-05 0.142821920093E-01-0.346011975369E-03
35 0.715507714946E-01 0.316132133031E-03-0.106581466521E-01-0.920513736900E-04 0.138018668842E-01-0.212219497066E-03
这里33、34、35是整数值,后面是6个双精度值。这些双精度值之间不能保证有空格或其他分隔符。i、 例如,如果一个双精度是负数,那么它前面将有一个“-”,这将占用空间。所以基本上,所有6个双值都可能在一起

现在的挑战是,如何优雅地提取这些信息

我尝试的是:

String.Split(' ');
这将不起作用,因为无法保证初始整数值与其余的双精度值之间存在空格

<>这可以用C++来解决,使用<代码> sSCANF
双a、b、c、d、e、f;
sscanf(字符串,“%d%lf%lf%lf%lf%lf”、&a、b、c、d、e和f);
//此处字符串包含文本文件中的一行数据。
包含双值的文本文件由第三方工具生成,我无法控制其输出


有没有办法一行一行地优雅地提取整数值和双数值?

用正则表达式解决这个问题。我的第一个镜头是:

"[\s-+]\d+\.\d+E[+-]\d\d"
我只是这样试过:

using System;
using System.Globalization;
using System.Text.RegularExpressions;

namespace ConsoleApp1 {

    class Program {
        static void Main(string[] args) {
            var fileContents =
                  "33 0.573140941467E-01 0.112914262390E-03 0.255553577735E-02 0.497192659486E-04 0.141869181079E-01-0.147813598922E-03"
                + "34 0.570076593453E-01 0.100112550891E-03 0.256427138318E-02-0.868691490164E-05 0.142821920093E-01-0.346011975369E-03"
                + "35 0.715507714946E-01 0.316132133031E-03-0.106581466521E-01-0.920513736900E-04 0.138018668842E-01-0.212219497066E-03";

            var rex = new Regex(@"[\s-+]\d+\.\d+E[+-]\d\d", RegexOptions.Multiline);
            foreach (Match match in rex.Matches(fileContents)) {
                double d = double.Parse(match.Value.TrimStart(), NumberFormatInfo.InvariantInfo);
                Console.WriteLine("found a match: " + match.Value.TrimStart() + " => " + d);
            }

            Console.ReadLine();
        }
    }
}
使用此输出(德语本地化,逗号作为小数分隔符):


如果我看对了,你有一个“固定宽度数据”格式。你可以简单地分析这个事实

i、 e.假设值位于文件
d:\temp\doubles.txt

void Main()
{
    var filename = @"d:\temp\doubles.txt";
    Func<string, string[]> split = (s) =>
    {
        string[] res = new string[7];
        res[0] = s.Substring(0, 2);
        for (int i = 0; i < 6; i++)
        {
            res[i + 1] = s.Substring(2 + (i * 19), 19);
        }
        return res;
    };
    var result = from l in File.ReadAllLines(filename)
                 let la = split(l)
                 select new
                 {
                    i = int.Parse(la[0]),
                     d1 = double.Parse(la[1]),
                     d2 = double.Parse(la[2]),
                     d3 = double.Parse(la[3]),
                     d4 = double.Parse(la[4]),
                     d5 = double.Parse(la[5]),
                     d6 = double.Parse(la[6])

                 };
    foreach (var e in result)
    {
        Console.WriteLine($"{e.i}, {e.d1}, {e.d2}, {e.d3}, {e.d4}, {e.d5}, {e.d6}");
    }
}

PS:有了准确的数据,
int
应该分配更多的空间。

到目前为止,我看到的答案非常复杂。这是一个没有过度思考的简单例子

根据@Veljko89的评论,我已经用无限数量的支持更新了代码

    List<double> ParseLine(string line)
    {
        List<double> ret = new List<double>();

        ret.Add(double.Parse(line.Substring(0, line.IndexOf(' '))));
        line = line.Substring(line.IndexOf(' ') + 1);

        for (; !string.IsNullOrWhiteSpace(line); line = line.Substring(line.IndexOf('E') + 4))
        {
            ret.Add(double.Parse(line.Substring(0, line.IndexOf('E') + 4)));
        }

        return ret;
    }
列表解析行(字符串行)
{
List ret=新列表();
ret.Add(double.Parse(line.Substring(0,line.IndexOf(“”)));
line=line.Substring(line.IndexOf(“”)+1);
对于(;!string.IsNullOrWhiteSpace(line);line=line.Substring(line.IndexOf('E')+4))
{
ret.Add(double.Parse(line.Substring(0,line.IndexOf('E')+4));
}
返回ret;
}

我只是做了非最佳选择,将“E-”字符串替换为其他字符串,而将所有负号替换为空格和负号(“-”),然后还原所有“E-”值

然后我可以使用split来提取值

private static IEnumerable<double> ExtractValues(string values)
{
    return values.Replace("E-", "E*").Replace("-", " -").Replace("E*", "E-").Split(' ').Select(v => double.Parse(v));
}
私有静态IEnumerable提取值(字符串值)
{
返回值。Replace(“E-”,“E*”)。Replace(“-”,“-”)。Replace(“E*”,“E-”)。Split(“”)。选择(v=>double.Parse(v));
}
您可以这样做:

public void ParseFile(string fileLocation)
{
   string[] lines = File.ReadAllLines(fileLocation);

   foreach(var line in lines)
   {
       string[] parts = var Regex.Split(line, "(?((?<!E)-)| )");

       if(parts.Any())
       {
          int first = int.Parse(parts[0]);

          double[] others = parts.Skip(1).Select(a => double.Parse(a)).ToArray();
       }
   }
}   
公共文件(字符串文件位置)
{
string[]lines=File.ReadAllLines(文件位置);
foreach(行中的var行)
{

string[]parts=var Regex.Split(line),(?((?如果我们不能使用
string.Split
我们可以借助
Regex.Split
;对于给定的

string line = @"  33 0.573140941467E-01 0.112914262390E-03 0.255553577735E-02 0.497192659486E-04 0.141869181079E-01-0.147813598922E-03";
我们可以试试

// Split either
//   1. by space
//   2. zero length "char" which is just after a [0..9] digit and followed by "-" or "+"
var items = Regex
  .Split(line, @" |((?<=[0-9])(?=[+-]))")
  .Where(item => !string.IsNullOrEmpty(item)) // we don't want empty parts 
  .Skip(1)                                    // skip 1st 33
  .Select(item => double.Parse(item));        // we want double

Console.WriteLine(string.Join(Environment.NewLine, items));
对于文本文件,我们应拆分每一行:

Regex regex = new Regex(@" |((?<=[0-9])(?=[+-]))");

var records = File
  .ReadLines(@"c:\MyFile.txt") 
  .Select(line => regex
     .Split(line)
     .Where(item => !string.IsNullOrEmpty(item))
     .Skip(1)
     .Select(item => double.Parse(item))
     .ToArray());

另一种解决方案是单独处理每一行并包括int值:

    static void Main(string[] args) {
        string[] fileLines = {
            "33 0.573140941467E-01 0.112914262390E-03 0.255553577735E-02 0.497192659486E-04 0.141869181079E-01-0.147813598922E-03",
            "34 0.570076593453E-01 0.100112550891E-03 0.256427138318E-02-0.868691490164E-05 0.142821920093E-01-0.346011975369E-03",
            "35 0.715507714946E-01 0.316132133031E-03-0.106581466521E-01-0.920513736900E-04 0.138018668842E-01-0.212219497066E-03"
        };

        var rex = new Regex(@"\b([-+]?\d+(?:\.\d+(?:E[+-]\d+)?)?)\b", RegexOptions.Compiled);
        foreach (var line in fileLines) {

            var dblValues = new List<double>();
            foreach (Match match in rex.Matches(line)) {
                string strVal = match.Groups[1].Value;
                double number = Double.Parse(strVal, NumberFormatInfo.InvariantInfo);
                dblValues.Add(number);
            }

            Console.WriteLine(string.Join("; ", dblValues));
        }

        Console.ReadLine();
    }
}

@CodeCaster-可能是双精度值,但起始整数肯定不是,因为它从1开始,可能会达到2000或更多。我需要同时提取整数和所有双精度值。所以这里的这一行是0.316132133031E-03-0.106581466521E-01-0.920513736900E-04…这是否意味着您有6个数字,其中5个“是的,6个双值,其中5个是负值。@栋克,请你澄清它是否是固定宽度的数据?”代码> %D/< >代码>双*< /代码>是C++中绝对未定义的行为。你发现哪个部分复杂?你见过程序员为每一个重复做一个单独的代码吗?事情?当你有15个变量或1000个变量的时候,你会怎么做?如果有这样的问题,试着这样想…明天新文件会有更多需要的变量,然后你会编写适用于所有变量的代码cases@CetinBasoz当你们不想弄乱处理器的缓存线时,是的,你们可以这样做。但你们是对的t因为这里不是这样。@CetinBasoz,您的代码也不会解决OP所问的问题,因为OP在注释中指出,第一个整数值可能会超过99,“…它从1开始,可能会达到2000…”。。。“@GurhanPolat,你愿意在结尾读我的评论吗?你可以简单地使用你需要的大小。在结尾,OP处理的是一个“固定宽度的数据”,需要用约束来处理。最好使用double.Parse(…,NumberFormatInfo.InvariantInfo),使用不变格式规范,因为这里的小数分隔符始终是一个点。当代码在具有不同区域设置(例如德语)的计算机上运行时如果忽略NumberFormatInfo.InvariantInfo,它将无法解析。否²,它仍然是真的-我刚刚尝试过。C#的方法很复杂:它默认为当前用户的区域设置!例如,第一个值“0.573140941467E-01”在我的德语系统中,当我省略NumberFormatInfo.InvariantInfo时,解析为双值57314094146,7。好的,我更正了,你可以将CultureInfo包含到解析中。很可能,他想将每一行作为单独的“数据记录”+1。但是我们可以改进正则表达式,使指数成为可选的。类似[\s-+]\d+\.\d+(E[-+]\d+)?
0.573140941467E-01
0.112914262390E-03
0.255553577735E-02
0.497192659486E-04
0.141869181079E-01
-0.147813598922E-03
Regex regex = new Regex(@" |((?<=[0-9])(?=[+-]))");

var records = File
  .ReadLines(@"c:\MyFile.txt") 
  .Select(line => regex
     .Split(line)
     .Where(item => !string.IsNullOrEmpty(item))
     .Skip(1)
     .Select(item => double.Parse(item))
     .ToArray());
  string[] test = new string[] {
     // your examples
     "  33 0.573140941467E-01 0.112914262390E-03 0.255553577735E-02 0.497192659486E-04 0.141869181079E-01-0.147813598922E-03",
     "  34 0.570076593453E-01 0.100112550891E-03 0.256427138318E-02-0.868691490164E-05 0.142821920093E-01-0.346011975369E-03",
     " 35 0.715507714946E-01 0.316132133031E-03-0.106581466521E-01-0.920513736900E-04 0.138018668842E-01-0.212219497066E-03",

     // Some challenging cases (mine)
     "    36 123+456-789    123e+78 9.9e-95 0.0001", 
  };

  Regex regex = new Regex(@" |((?<=[0-9])(?=[+-]))");

  var records = test
    .Select(line => regex
      .Split(line)
      .Where(item => !string.IsNullOrEmpty(item))
      .Skip(1)
      .Select(item => double.Parse(item))
      .ToArray());

  string testReport = string.Join(Environment.NewLine, records
    .Select(record => $"[{string.Join(", ", record)}]"));

  Console.WriteLine(testReport);
[0.0573140941467, 0.00011291426239, 0.00255553577735, 4.97192659486E-05, 0.0141869181079, -0.000147813598922]
[0.0570076593453, 0.000100112550891, 0.00256427138318, -8.68691490164E-06, 0.0142821920093, -0.000346011975369]
[0.0715507714946, 0.000316132133031, -0.0106581466521, -9.205137369E-05, 0.0138018668842, -0.000212219497066]
[123, 456, -789, 1.23E+80, 9.9E-95, 0.0001]
    static void Main(string[] args) {
        string[] fileLines = {
            "33 0.573140941467E-01 0.112914262390E-03 0.255553577735E-02 0.497192659486E-04 0.141869181079E-01-0.147813598922E-03",
            "34 0.570076593453E-01 0.100112550891E-03 0.256427138318E-02-0.868691490164E-05 0.142821920093E-01-0.346011975369E-03",
            "35 0.715507714946E-01 0.316132133031E-03-0.106581466521E-01-0.920513736900E-04 0.138018668842E-01-0.212219497066E-03"
        };

        var rex = new Regex(@"\b([-+]?\d+(?:\.\d+(?:E[+-]\d+)?)?)\b", RegexOptions.Compiled);
        foreach (var line in fileLines) {

            var dblValues = new List<double>();
            foreach (Match match in rex.Matches(line)) {
                string strVal = match.Groups[1].Value;
                double number = Double.Parse(strVal, NumberFormatInfo.InvariantInfo);
                dblValues.Add(number);
            }

            Console.WriteLine(string.Join("; ", dblValues));
        }

        Console.ReadLine();
    }
}
33; 0,0573140941467; 0,00011291426239; 0,00255553577735; 4,97192659486E-05; 0,0141869181079; -0,000147813598922
34; 0,0570076593453; 0,000100112550891; 0,00256427138318; -8,68691490164E-06; 0,0142821920093; -0,000346011975369
35; 0,0715507714946; 0,000316132133031; -0,0106581466521; -9,205137369E-05; 0,0138018668842; -0,000212219497066