Чтение двойных значений из текстового файла
Попытка чтения данных из текстового файла с помощью приложения С#. Существует несколько строк данных, и каждая из них начинается с целого числа, а затем идет группа двойных значений. Часть текстового файла выглядит следующим образом,
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 двойных значений. И эти двойные значения не обязательно имеют пробел или какой-либо другой разделитель между ними. то есть, если двойное число отрицательно, то перед ним будет "-", и это займет место. Таким образом, в принципе, возможно, что все 6 двойных значений будут вместе.
Теперь проблема в том, как извлечь это изящно?
Что я пробовал:
String.Split(' ');
Это не сработает, поскольку не гарантируется пробел между начальными целочисленными значениями и остальными двойными значениями.
Это может быть легко решено в C++ с помощью sscanf
.
double a, b, c, d, e, f;
sscanf(string, "%d %lf%lf%lf%lf%lf%lf", &a, &b, &c, &d, &e, &f);
// here string contains a line of data from text file.
Текстовый файл, содержащий двойные значения, создается сторонним инструментом, и я не могу контролировать его вывод.
Есть ли способ, как целые и двойные значения могут быть изящно извлечены построчно?
Ответы
Ответ 1
Если я вижу это правильно, у вас есть формат данных с фиксированной шириной. Чем вы можете просто проанализировать этот факт.
т.е. если предположить, что значения находятся в файле 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}");
}
}
Выходы:
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
PS: С вашими точными данными int
должно выделять больше места.
Ответ 2
Решите это с помощью регулярного выражения. Мой первый снимок:
"[\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();
}
}
}
С этим выводом (немецкая локализация, с запятой в качестве десятичного разделителя):
found a match: 0.573140941467E-01 => 0,0573140941467
found a match: 0.112914262390E-03 => 0,00011291426239
found a match: 0.255553577735E-02 => 0,00255553577735
found a match: 0.497192659486E-04 => 4,97192659486E-05
found a match: 0.141869181079E-01 => 0,0141869181079
found a match: -0.147813598922E-03 => -0,000147813598922
found a match: 0.570076593453E-01 => 0,0570076593453
found a match: 0.100112550891E-03 => 0,000100112550891
found a match: 0.256427138318E-02 => 0,00256427138318
found a match: -0.868691490164E-05 => -8,68691490164E-06
found a match: 0.142821920093E-01 => 0,0142821920093
found a match: -0.346011975369E-03 => -0,000346011975369
found a match: 0.715507714946E-01 => 0,0715507714946
found a match: 0.316132133031E-03 => 0,000316132133031
found a match: -0.106581466521E-01 => -0,0106581466521
found a match: -0.920513736900E-04 => -9,205137369E-05
found a match: 0.138018668842E-01 => 0,0138018668842
found a match: -0.212219497066E-03 => -0,000212219497066
Ответ 3
Я просто выбрал неоптимальный вариант и заменил строку "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));
}
Ответ 4
void Main()
{
var filename = @"d:\temp\doubles.txt";
Func<string, string[]> split = (s) =>
{
string[] str= new string[7];
str[0] = s.Substring(0, 2);
for (int i = 0; i < 6; i++)
{
str[i + 1] = s.Substring(2 + (i * 19), 19);
}
return str;
};
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}");
}
}
Ответ 5
Вы можете сделать это:
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();
}
}
}
Ответ 6
Ответы, которые я видел до сих пор, настолько сложны. Вот простой, не задумываясь,
Согласно комментарию @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;
}
Ответ 7
Если мы не можем использовать string.Split
, мы можем попытаться разбить регулярные выражения с помощью Regex.Split
; для данного line
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));
и получить
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());
Demo:
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]
Ответ 8
Еще одно решение, обрабатывающее каждую строку отдельно и включающее значение 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();
}
}
Результат/вывод:
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