Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Общие вопросы по .NET и C# > чтение из файла float значений


Автор: r00t_aka_spin 29.9.2007, 23:47
Есть текстовый файл содержащий числа с плавающей точкой

Код

-5.21025937050581E-0001  2.32213917188346E-0001
-2.99051026813686E+0000  5.73992113117129E+0000
 4.49050202965736E+0000  7.32700684573501E+0000
 9.27351134829223E+0000  1.82522133458406E+0000
-4.59152134135365E+0000 -8.66918832529336E+0000
-1.79429247044027E+0000 -1.68778934981674E+0000


Требуется считать эти числа во float переменные. 

P.S. Знаю что можно выдернуть всё регексами, но каждый раз это  делать надоедает. Не верю что нет встроеных распознавалок чисел на уровне считывания, как например в Delphi или в C++

Автор: marcusmae 30.9.2007, 00:19
Ты имеешь в виду типа
Код

fscanf(file, "%f %f", &num1, &num2);

?

Да, думаю, таких функций нет. Но и
Цитата(r00t_aka_spin @  29.9.2007,  23:47 Найти цитируемый пост)
выдернуть всё регексами

- явный перебор по сложности, ведь можно читать построчно, а для выделения значений пользоваться Split-ом, как показано, например, http://www.thescripts.com/forum/thread268853.html.

Если совсем не годится использовать двоичные/типизированные файлы, то можно выкрутиться, используя Managed C++ (для них даже появились более надёжные версии с суффиксом _s - fscanf_s), написать обёртку и использовать в C#.

Автор: r00t_aka_spin 30.9.2007, 00:34
Код

fscanf(file, "%f %f", &num1, &num2);


да, именно это и имею в виду. 
Большое спасибо за Split, забыл про него. привык всё регексами делать.

Автор: marcusmae 30.9.2007, 01:26
Хм, да не за что  smile
Несмотря на то, что Вы уже вполне довольны, я таки осуществил свою угрозу по написанию обёртки :

Код

#pragma once

#include "stdio.h"

using namespace System;

namespace LegacyFileScanning {

    class NativeFile  {

    public :
        FILE* stream;

    public :
        int lastError;

        NativeFile(char* path) {
            this->lastError = 0;
            if ( fopen_s(&stream, path, "r") != 0 )
                lastError = -1;
        }

        ~NativeFile(void) { fclose(stream); }
    };

    public ref class File {

    private :
        NativeFile* nativeFile;

    public :
        void ScanF(String^ format, array<float>^ %args) {
            // Split format string into tokens by space.
            array<String^>^ tokens = format->Split(nullptr);

            for (int tokenIndex = 0; tokenIndex < tokens->Length; tokenIndex++)
            {
                // Promote token to char-string.
                char* chToken = new char[tokens[tokenIndex]->Length];
                for (int index = 0; index < tokens[tokenIndex]->Length; index++)
                    chToken[index] = (char)tokens[tokenIndex][index];
                chToken[tokens[tokenIndex]->Length] = '\0';

                float val;
                fscanf_s(this->nativeFile->stream, chToken, &val);
                args[tokenIndex] = val;
            }
        }

        File(String^ path) {
            // Convert path.
            char* chPath = new char[path->Length + 1];
            for (int index = 0; index < path->Length; index++)
                chPath[index] = (char)path[index];
            chPath[path->Length] = '\0';

            this->nativeFile = new NativeFile(chPath);
            if (this->nativeFile->lastError == -1)
                throw gcnew ArgumentException("Cannot open the specified file.");
        }

        ~File(void) { delete this->nativeFile; }
    };
}


Тут написан native класс NativeFile, в котором хранится дескриптор файла и его managed сателлит - класс File, в котором есть функция ScanF, которая делает частное применение fscanf для вещественных данных.

Далее, тестовая программка уже на диезе :

Код

using System;
using System.Collections.Generic;
using System.Text;
using LegacyFileScanning;

namespace TestApp
{
    class Program
    {
        static string path = "data.txt";

        static void Main(string[] args)
        {
            File file = new File(path);
            float[] values = new float[2];
            file.ScanF("%f %f", ref values);

            Console.WriteLine(values[0]);
            Console.WriteLine(values[1]);
            Console.ReadKey();
        }
    }
}


Output для ваших данных :
Код

-0.521026
0.2322139


Как и требовалось, считалась первая строчка файла  smile

Это решение, конечно, "не для прессы", между нами  smile  Однако, вполне рабочее. При большом желании, его можно значительно приблизить к настоящему fscanf, применив список аргументов переменной длины (params - в плюсах это будет три точки : ...) и по-аккуратнее конвертируя wide-строки в мультибайтовые.

Impossible is nothing!

В прикреплённом архиве - весь solution c двумя проектами. Тока скомпильте заново, а то пришлось подрезать бинарники из-за ограничения размера...

Автор: r00t_aka_spin 30.9.2007, 19:20
я в C++ не особо силён, но постараюсь разобраться в работе класса, добавлю так же возможность считывать int и другие численные типы. ещё раз большое спасибо.

Автор: okaton 1.10.2007, 18:32
r00t_aka_spin, не знаю актуально для вас это сейчас или нет

Код

float[] shorts;
            using (BinaryReader br = new BinaryReader(File.OpenRead("Single.Float")))
            {
                List<float> res = new List<float>();
                for (Int32 i = 0; br.BaseStream.Position < br.BaseStream.Length; i++)
                    res.Add(br.ReadSingle());
                shorts = res.ToArray();                
            }    

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)