有效而干净的方法来编写一个ofstream/fstream对象,以用标头编写表

Efficient and clean way to write an ofstream/fstream object for writing tables with headers

本文关键字:对象 ofstream fstream 一个 方法 有效      更新时间:2023-10-16

在试图成为一个没有意大利面代码的真正优秀和专业的程序员的过程中,我面临着一些情况,无论我做什么,代码都看起来凌乱,而且我无法使用我的功能编程技能和面向对象的编程技能,以编写一个很好的有组织的代码而没有重复模式,我今天试图解决这样的问题。

因此,我有一个控制台/终端程序,该程序可以从许多设备中测量许多东西,并且输出是一个用于文本文件的ASCII表。我关心的是:如何以最灵活和专业的方式传递此信息。

所以我按以下方式编写输出的标题(名称,单位和数据类型的三行):

    ofstream outputStream ("output.txt",std::ios::out);
    outputStream << "#:" <<
                   "timestamp________________" << "t" <<
                   "ActionID" << "t" <<
                   "targetR" << "t" << "targetPhi" << "t" << "targetZ" << "t" << "pinstate" <<"t" << "fluxgate_orient" <<"t" <<
                   "fluxXMean" << "t" << "fluxYMean" << "t" << "fluxZMean" << "t" <<
                   "fluxXStdDev" << "t" << "fluxYStdDev" << "t" << "fluxZStdDev" << "t" <<
                   "fluxNumOfSamples" << "t" <<
                   "Incl1RollMean" << "t" << "Incl1TiltMean" << "t" << "Incl2RollMean" << "t" << "Incl2TiltMean" << "t" <<
                   "Incl1RollStdDev" << "t" << "Incl1TiltStdDev" << "t" << "Incl2RollStdDev" << "t" << "Incl2TiltStdDev" << "t" <<
                   "InclNumOfSamples" << "t" <<
                   "PotRMean" << "t" << "PotPhiMean" << "t" << "PotZMean" << "t" <<
                   "PotFullMean" << "t" <<
                   "PotRStdDev" << "t" << "PotPhiStdDev" << "t" << "PotZStdDev" << "t" <<
                   "PotFullStdDev" << "t" <<
                   "PotNumOfSamples" << "t" <<
                   "IntegrationTime" << "t" <<
                   "CsBeginFIDSetIndex" << "t" <<
                   "LengthOfReceivedStr" << "t" <<
                   "CsFullReceivedStr" << "t" <<
                   "ID" << "t" <<
                   "Comment" << std::endl;
outputStream << "#=" <<
                   "YYYY-MM-DD_HH:MM:SS.3_UTC" << "t" <<
                   "unitless" << "t" <<
                   "mm" << "t" << "deg" << "t" << "mm" << "t" << "enum" <<"t" << "unitless" <<"t" <<
                   "nT" << "t" << "nT" << "t" << "nT" << "t" <<
                   "nT" << "t" << "nT" << "t" << "nT" << "t" <<
                   "unitless" << "t" <<
                   "mrad" << "t" << "mrad" << "t" << "mrad" << "t" << "mrad" << "t" <<
                   "mrad" << "t" << "mrad" << "t" << "mrad" << "t" << "mrad" << "t" <<
                   "unitless" << "t" <<
                   "mm" << "t" << "deg" << "t" << "mm" << "t" <<
                   "V" << "t" <<
                   "mm" << "t" << "deg" << "t" << "mm" << "t" <<
                   "V" << "t" <<
                   "unitless" << "t" <<
                   "seconds" << "t" <<
                   "unitless" << "t" <<
                   "unitless" << "t" <<
                   "unitless" << "t" <<
                   "unitless" << "t" <<
                   "unitless" << std::endl;
outputStream << "#%" <<
                   "string__________________" << "t" <<
                   "ulong" << "t" <<
                   "double" << "t" << "double" << "t" << "double" << "t" << "char" <<"t" << "int" <<"t" <<
                   "double" << "t" << "double" << "t" << "double" << "t" <<
                   "double" << "t" << "double" << "t" << "double" << "t" <<
                   "ulong" << "t" <<
                   "double" << "t" << "double" << "t" << "double" << "t" << "double" << "t" <<
                   "double" << "t" << "double" << "t" << "double" << "t" << "double" << "t" <<
                   "ulong" << "t" <<
                   "double" << "t" << "double" << "t" << "double" << "t" <<
                   "double" << "t" <<
                   "double" << "t" << "double" << "t" << "double" << "t" <<
                   "double" << "t" <<
                   "ulong" << "t" <<
                   "double" << "t" <<
                   "ulonglong" << "t" <<
                   "ulong" << "t" <<
                   "string" << "t" <<
                   "string" << "t" <<
                   "string" << std::endl;

之后,数据循环中的数据是:

while(devices_are_recording)
{
        outputStream <<
                           std::setprecision(defaultOutputAccuracy.getValue()) << GetTime_Qt() << "t" <<
                           std::setprecision(defaultOutputAccuracy.getValue()) << coordinatesData[coordinatesIndex].actionID << "t" <<
                           std::setprecision(positionOutputAccuracy.getValue()) << nemaMotors.getPosition_internal(R_CONTROLLER_AXIS_INDEX) << "t" << nemaMotors.getPosition_internal(PHI_CONTROLLER_AXIS_INDEX) << "t" << nanotecMotor.getPosition_internal() << "t" << pin.getValue().getPinState() <<"t" << GetFluxgateOrientiation(pin,allowedPinRVals,tolRPin.getValue()) << "t" <<
                           std::setprecision(defaultOutputAccuracy.getValue()) << fluxgateMean.getValue().r << "t" << fluxgateMean.getValue().phi << "t" << fluxgateMean.getValue().z << "t" <<
                           std::setprecision(defaultOutputAccuracy.getValue()) << fluxgateStdDev.getValue().r << "t" << fluxgateStdDev.getValue().phi << "t" << fluxgateStdDev.getValue().z << "t" <<
                           std::setprecision(defaultOutputAccuracy.getValue()) << samplesRecorded_fluxgate/ (enableBuffering ? (fluxgateNumberOfChannels*2) : 1) << "t" <<
                           std::setprecision(defaultOutputAccuracy.getValue()) << inclinometerMean.getValue()[0] << "t" << inclinometerMean.getValue()[1] << "t" << inclinometerMean.getValue()[2] << "t" << inclinometerMean.getValue()[3] << "t" <<
                           std::setprecision(defaultOutputAccuracy.getValue()) << inclinometerStdDev.getValue()[0] << "t" << inclinometerStdDev.getValue()[1] << "t" << inclinometerStdDev.getValue()[2] << "t" << inclinometerStdDev.getValue()[3] << "t" <<
                           std::setprecision(defaultOutputAccuracy.getValue()) << samplesRecorded_inclinometer << "t" <<
                           std::setprecision(positionOutputAccuracy.getValue()) << potPosMean.getValue().r << "t" << potPosMean.getValue().phi << "t" << potPosMean.getValue().z << "t" <<
                           std::setprecision(defaultOutputAccuracy.getValue()) << directSum_potentiometers[3]/samplesRecorded_potentiometers << "t" <<
                           std::setprecision(defaultOutputAccuracy.getValue()) << potPosStdDev.getValue().r << "t" << potPosStdDev.getValue().phi << "t" << potPosStdDev.getValue().z << "t" <<
                           std::setprecision(defaultOutputAccuracy.getValue()) << sqrt((directSquaresSum_potentiometers[3] -  directSum_potentiometers[3]*directSum_potentiometers[3]/samplesRecorded_potentiometers ) / (samplesRecorded_potentiometers-1))  << "t" <<
                           std::setprecision(defaultOutputAccuracy.getValue()) << samplesRecorded_potentiometers << "t" <<
                           std::setprecision(defaultOutputAccuracy.getValue()) << coordinatesData[coordinatesIndex].measurementDuration << "t" <<
                           std::setprecision(defaultOutputAccuracy.getValue()) << CsCurrentIndex << "t" <<
                           std::setprecision(defaultOutputAccuracy.getValue()) << CsLengthOfStrReceived << "t" <<
                           std::setprecision(defaultOutputAccuracy.getValue()) << CsFullReceivedStr << "t" <<
                           std::setprecision(defaultOutputAccuracy.getValue()) << coordinatesData[coordinatesIndex].coordinatesIdentifier << "t" <<
                           std::setprecision(defaultOutputAccuracy.getValue()) << coordinatesData[coordinatesIndex].comment << "t" <<
                           endl;
}

您看到的,有很多列,它们都必须对齐...列的更改很容易在输出中引入错误,因为我自己计算了列。

在C 中写这样的东西的最佳方法是什么?

您可以在这里使用多态性:定义基类

struct quantity {
    virtual std::string name();
    virtual std::string unit();
    virtual std::string datatype();
    virtual std::string measurement();
}

并定义了针对每个数量的适当实现的派生类。

然后,您可以拥有一个std::vector<std::unique_ptr<quantity>> q;,并且标头的打印和测量值变得很容易在q上进行循环。

对于第一个问题,一个建议是将标签的印刷封装在函数中:

1)将列名存储在常数std::arraystd::vector

#include<vector>

const std::vector<std::string> col_labels = {"col_label1, col_label2, col_label3"};

因此,您可能会得到类似的东西:

void printHeaders(std::ofstream& outputStream){
    const std::vector<std::string> col_labels = {"col_label1", 
                                                "col_label2", 
                                                "col_label3"};
    for(auto& label: col_labels){
        outputStream << label << "t";
    }
}

2)在第二个方面,如果您具有不同的打印模式,例如,可以使用std::function将函数作为参数传递给其他功能,并对模式有更多控制权。您可以将功能声明为参数或使用lambda表达式。

注意:您将需要编译器中的C 11支持。在g++中使用std=c++11选项。

例如,假设您有两个不同的模式:a)将字段值包装在*中b)将字段值包装在( )中c)将字段值包装在-- --

#include<fstream>
#include<functional>
#include<string>
std::string formatAsterix(const std::string& str){
    //bad design concat std::strings... just to show the idea
    return std::string("*") + str + std::string("*");  
} 
std::string formatParenthesis(const std::string& str){
    return std::string("(") + str + std::string(")"); 
} 
void printFormatted(std::ofstream& outputStream, /*Your output stream*/
                    const std::string str, /*your string*/
                    std::function<std::string(const std::string&)> formatter ) /*your formatter function*/
{
    outputStream << formatter(str) << std::endl;
}

int main(){
    std::ofstream outputStream; 
    outputStream.open("c:\tmp\out.txt");
    printFormatted(outputStream, "FIELD_VALUE", formatAsterix);
    printFormatted(outputStream, "FIELD_VALUE", formatParenthesis);
    // This last one uses a lamba expression
    printFormatted(outputStream, "FIELD_VALUE", 
    [&](const std::string str)
    { 
       return std::string("-- ") + str + std::string(" --");    
    });

    outputStream.close();
}

执行程序,您将获得:

*FIELD_VALUE*
(FIELD_VALUE)
-- FIELD_VALUE --

我希望这些示例可以向您展示C 中功能编程的测试。