analiza statyczna - projekt

0

Witam,
Mam zamiar napisać aplikację której zadaniem będzie obliczyć średnią prędkość wiatru w Styczniu 2012 roku na podstawie danych dostarczonych w załączonym pliku.
Kod będę wstawiał progresywnie, tak żeby nie zniechęcać do jego przeglądania. Myślę, że popełniam sporo błędów nie zdając sobie z tego sprawy dlatego zależy mi by końcowy kod był solidny, najlepiej z użyciem wzorców projektowych.

Na początek klasa której zadaniem jest otwarcie pliku i udostępnienie całej jego zawartości.

#ifndef FILEFETCHER_H
#define FILEFETCHER_H

#include <string>
#include <fstream>
#include <streambuf>
#include <memory>

class FileFetcher
{
public:
    explicit FileFetcher(const std::string& filePath);
    std::string& getRawString() const;
    const std::string& getFilePath() const;
private:
    const std::string filePath;
    std::unique_ptr<std::string> rawString;
};

#endif // FILEFETCHER_H

#include "filefetcher.h"
#include <string>
#include <fstream>
#include <streambuf>

FileFetcher::FileFetcher(const std::string& path) : filePath(path)
{
    std::ifstream t(filePath);
    rawString = std::unique_ptr<std::string>{new std::string((std::istreambuf_iterator<char>(t)),
                                                             std::istreambuf_iterator<char>())};
}

std::string& FileFetcher::getRawString() const
{
    return *rawString;
}

const std::string& FileFetcher::getFilePath() const
{
    return filePath;
}

Poprawiona według komentarzy.

0

Poprawiłem klasę FileFetcher Następna klasa odpowiada za analizę danych.

#ifndef ANALYZEDATA_H
#define ANALYZEDATA_H

#include "filefetcher.h"
#include <vector>
#include <memory>

class AnalyzeData
{
public:
    explicit AnalyzeData(const std::string& stringPtr);
    void parseData(const std::string& date);
    double calculateMean(const std::vector<double>& data);
    double getMeanWindSpeed();
private:
    const std::string& rawString;
    std::vector<double> windSpeedVector;
};

#endif // ANALYZEDATA_H

#include "analyzedata.h"
#include <iostream>
#include <sstream>
#include <numeric>
#include <algorithm>

AnalyzeData::AnalyzeData(const std::string &stringPtr) : rawString(stringPtr) {}

void AnalyzeData::parseData(const std::string& inputDate)
{
    std::istringstream* iss = new std::istringstream(rawString);
    std::string line;
    while (std::getline(*iss, line))
    {
        std::vector<std::string> tmp;
        std::string date;
        std::string restOfTheData;
        std::stringstream ss(line);

        std::getline(ss, date, '\t');
        while (std::getline(ss, restOfTheData, '\t'))
        {
            tmp.push_back(restOfTheData);
        }

        if(date.find(inputDate) != std::string::npos)
        {
            double pairWind(atof((tmp.back()).c_str()));
            windSpeedVector.push_back(pairWind);
        }
    }
}

double AnalyzeData::calculateMean(const std::vector<double>& data)
{
    double sum = std::accumulate(data.begin(), data.end(), 0.0);
    double mean = sum / data.size();

    return mean;
}

double AnalyzeData::getMeanWindSpeed()
{
    return calculateMean(windSpeedVector);
}

Trochę mi się nie podoba to, że tworzę tyle zmiennych które nie są mi potrzebne, ale szczerze to nie wiem jak efektywniej wczytać te dane.
EDIT: Poprawione, chodź dalej nie wiem czy to optymalne rozwiązanie.

1

Ja zadam pierwsze pytanie za 10 pkt.
1.Czy masz testy UT do tego jakieś?
2. tego typu konstrukcje, lepiej może zamykać sobie w jakieś struktury, ale ty i tak z tego co widzę korzystasz z jednego parametru po co ładować pozostałe?

        std::string         date;
        std::string airTemp;
        std::string barometricPress;
        std::string dewPint;
        std::string relativeHumidity;
        std::string windDir;
        std::string windGust;
        std::string windSpeed;
  1. Pytanie, czy musisz korzystać z płaskiego pliku tekstowego? Może warto rozważyć .xml albo baze danych sql(np. sqllite).
  2. Ogólnie cała to operacja trochę dziwnie wygląda. Podaj nam strukturę pliku z którego czytasz.
    edit:
    http://clang.llvm.org/extra/clang-tidy/
0
  1. Nie mam Unit Testów.
  2. Nie potrafię wczytać tylko windSpeed nie czytając po drodze innych parametrów.
  3. Póki co chciałbym zwykły plik tekstowy, przyjdzie czas na sqllite
  4. struktura pliku:
    date time Air_Temp Barometric_Press Dew_Point Relative_Humidity Wind_Dir Wind_Gust Wind_Speed
    2012_01_01 00:02:14 34.30 30.50 26.90 74.20 346.40 11.00 3.60
    2012_01_01 00:08:29 34.10 30.50 26.50 73.60 349.00 12.00 8.00

Brakuje mi wiedzy i doświadczenia, żeby rozwiązanie tego problemu było bardziej przejrzyste i czytelniejsze.

1
  1. To napisz, bo testy to nie tylko testowanie kodu ale też rozmyślanie nad interfejsami. Wpisz w google gtest example github. Tak będziesz miał przykładowy projekt, możesz go użyć jako podstawy do integracji UT. Do tego pouczysz się cmake.
  2. Spójrz na takie rozwiązanie https://stackoverflow.com/questions/6182147/how-to-getline-from-specific-line-in-a-file-c defacto wczytujesz wszystko w kolejkę. I masz dostęp do dowolnych danych.
  3. Zauważ Wind_Speed jest na końcu, czyli de facto trzeba by czytać od tyłu do napotkania pierwszego \t, pozostałych danych byś nie wczytywał.
    edit:
    Jedna uwaga na przyszłość. Takie machanie stringami tzn. dopisywanie wielokrotnie wielu rzeczy do stringa itd. Jak się kończy bufor w stringu to trzeba alokować nową pamięć, a to kosztuje czas.
0

Wklejam całość, chętnie usłyszę kilka słów krytyki. Aplikacja ma wyliczać średnią prędkość wiatru w danym miesiącu korzystając z danych pobieranych z pliku który znajduje się w załączniku. Dane wyświetlane mają być w formacie json. Dodam tylko, że to nie jest zadanie na jakies kolokwium itp.. Chciałbym się czegoś nauczyć, dlatego wymyślam sobie różne zadania.
FileFetcher

#ifndef FILEFETCHER_H
#define FILEFETCHER_H

#include <string>
#include <fstream>
#include <streambuf>
#include <memory>

class FileFetcher
{
public:
    explicit FileFetcher(const std::string& filePath);
    std::string& getRawString() const;
    const std::string& getFilePath() const;
private:
    const std::string filePath;
    std::unique_ptr<std::string> rawString;
};

#endif // FILEFETCHER_H

#include "filefetcher.h"
#include <string>
#include <fstream>
#include <streambuf>

FileFetcher::FileFetcher(const std::string& path) : filePath(path)
{
    std::ifstream t(filePath);
    rawString = std::unique_ptr<std::string>{new std::string((std::istreambuf_iterator<char>(t)),
                                                             std::istreambuf_iterator<char>())};
}

std::string& FileFetcher::getRawString() const
{
    return *rawString;
}

const std::string& FileFetcher::getFilePath() const
{
    return filePath;
}

AnalyzeData

#ifndef ANALYZEDATA_H
#define ANALYZEDATA_H

#include "filefetcher.h"
#include <vector>
#include <memory>

class AnalyzeData
{
public:
    explicit AnalyzeData(const std::string& stringPtr);
    void parseData(const std::string& date);
    double calculateMean(const std::vector<double>& data);
    double getMeanWindSpeed();
private:
    const std::string& rawString;
    std::vector<double> windSpeedVector;
};

#endif // ANALYZEDATA_H

#include "analyzedata.h"
#include <iostream>
#include <sstream>
#include <numeric>
#include <algorithm>

AnalyzeData::AnalyzeData(const std::string &stringPtr) : rawString(stringPtr) {}

void AnalyzeData::parseData(const std::string& inputDate)
{
    std::istringstream* iss = new std::istringstream(rawString);
    std::string line;
    while (std::getline(*iss, line))
    {
        std::vector<std::string> tmp;
        std::string date;
        std::string restOfTheData;
        std::stringstream ss(line);

        std::getline(ss, date, '\t');
        while (std::getline(ss, restOfTheData, '\t'))
        {
            tmp.push_back(restOfTheData);
        }

        if(date.find(inputDate) != std::string::npos)
        {
            double windSpeed(atof((tmp.back()).c_str()));
            windSpeedVector.push_back(windSpeed);
        }
    }
}

double AnalyzeData::calculateMean(const std::vector<double>& data)
{
    double sum = std::accumulate(data.begin(), data.end(), 0.0);
    double mean = sum / data.size();

    return mean;
}

double AnalyzeData::getMeanWindSpeed()
{
    return calculateMean(windSpeedVector);
}

JSonParser

#ifndef JSONPARSER_H
#define JSONPARSER_H

#include <string>
#include <memory>
#include "analyzedata.h"

static const char * _json_format =
    "{ \"%s\": {\n"
    "  \"windSpeed\": { \"mean\": %lf }\n"
    "} }\n"
;

class JsonParser
{
public:
    JsonParser(AnalyzeData& dataRef);
    std::string getJsonData(const char* date,double windSpeed);
    double getMeanWindSpeed() const;

    template< typename... argv >
    std::string stringf( const char* format, argv... args ) {
        const size_t SIZE = std::snprintf( NULL, 0, format, args... );
        std::string output;
        output.resize(SIZE);
        std::snprintf( &(output[0]), SIZE+1, format, args... );
        return std::move(output);
    }

private:
    AnalyzeData& data;
};

#endif // JSONPARSER_H
#include "jsonparser.h"

JsonParser::JsonParser(AnalyzeData& dataRef) : data(dataRef)
{

}

std::string JsonParser::getJsonData(const char* date,double windSpeed)
{
    std::string JsonData = stringf(_json_format,date,windSpeed, 0.00);
    return std::move(JsonData);
}

double JsonParser::getMeanWindSpeed() const
{
    return data.getMeanWindSpeed();
}

main.cpp

#include <iostream>
#include "filefetcher.h"
#include "analyzedata.h"
#include "jsonparser.h"
#include <memory>

using namespace std;

int main(int argc, char *argv[])
{
    std::unique_ptr<FileFetcher> fd(new FileFetcher("Environmental_Data_Deep_Moor_2012.txt"));
    std::unique_ptr<AnalyzeData> dataAnalizator(new AnalyzeData(fd->getRawString()));
    dataAnalizator->parseData("2012_01_05");
    JsonParser* jsonParser = new JsonParser(*dataAnalizator);
    const std::string& output = jsonParser->getJsonData("2012_01_01",jsonParser->getMeanWindSpeed());
    std::cout << output << std::endl;
    return 0;
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1