C# 从文本文件中读取双值
尝试使用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
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