【问题标题】:C++ 2D Struct Array dataC++ 2D 结构数组数据
【发布时间】:2016-03-19 00:45:31
【问题描述】:

我希望能够将文件中的值存储到用户定义的结构中。我想要拥有包含不同类型数据的列,例如:用于字符名称的 std::string、用于字符健康的浮点数和用于字符体验的 int。

我想要的输出是一个 6 x 3 的数组,如下所示

 Alice    23.4      3210 
 Xander   45.3      1110
 Bernard  12.9      2024
 Yanni    23.7      1098 
 Craw     50.5      980
 Zack     11.9      1024

这是我迄今为止尝试过的:

struct charData
{
    string charName;
    float charHealth;
    int charExp;
};



int main() {
    const int NUM_COLS = 3;
    int NUM_ROWS = 6;
    int charNumber = 0;
    int userInput;
    int loop = 0;
    int i,j;
    string line;

    ifstream myIn;    // Declare read file stream
    myIn.open("party.dat"); // Open file    

    struct charData charArray[NUM_ROWS][NUM_COLS];

    while( !myIn.eof() ) {
        for ( j = 0; j <  NUM_COLS ; j++) {
            for ( i = 0; i < NUM_ROWS ; i++) {
                 myIn >> charArray[i][j].charName;    
                 myIn >> charArray[i][j].charHealth;
                 myIn >> charArray[i][j].charExp;    
            }    
        }    
    }

    return 0;    
}

我还计划允许用户按每个列类型对数据进行排序。要按字母顺序对名称进行排序,按健康和/或经验排序:我正在考虑使用二维数组。这会是最好的选择吗?

【问题讨论】:

  • 为什么你需要一个二维数组而不仅仅是charData的数组?
  • 对不起,如果我说了什么奇怪的话。我是 C++ 菜鸟。我希望将信息保存在某种行和列中
  • 此问题不符合minimal reproducible example 的要求。请编辑您的问题,使其包含minimal reproducible example。在您这样做之前,您不太可能得到权威的答案。
  • 我还计划允许用户按列对数据进行排序,一种选择是按字母顺序对名称进行排序,另外两种是通过 int 和整数数据,我认为是二维数组将是最好的选择
  • 这行代码:struct charData charArray[NUM_ROWS][NUM_COLS]; 应该是这样的:charData charArray[NUM_ENTRIES]; 其中NUM_ENTRIES 等于文件中的行数。然后要打印,您只需要一个 for 循环来遍历每个条目,其中数组的索引值是您的结构的单个实例。类似的 for 循环也可用于将其显示到控制台。

标签: c++ arrays struct


【解决方案1】:

我认为您将结构中的字段数与数据列数混淆了。您仅显示 6 条记录的数据,每条记录有 3 个字段。尝试将 NUM_COLS 设置为 1,看看它是否更适合您。

或者,完全摆脱数组的第二维 (j)。

【讨论】:

  • 我将 j 设置为 1 并且它起作用了.. 不知道为什么要试图绕着它转。不过,我的想法是拥有一个 6x3 阵列。我要编辑我的帖子以获得更好的解释
【解决方案2】:

我不知道您是以二进制形式还是以文本形式读取文件;但在我的回答中,我从一个文本文件中读取,其中一行文本中的每个字段都由空格字符分隔。我将包括编译和构建您正在寻找的正确工作程序所需的所有文件。其中一些包括可能不需要,但在我的解决方案中,因为我有更大的项目依赖于这些来正确构建。

stdafx.h

#ifndef STDAFX_H
#define STDAFX_H

#include <Windows.h>

#include <stdio.h>
#include <tchar.h>
#include <conio.h>

#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <vector>
#include <array>
#include <memory>

#include <queue>
#include <functional>

#include <algorithm>

// User Application Specific
// #include "ExceptionHandler.h" // Required For My Solution But Isn't Used In Yours

namespace util {

enum ReturnCode {
    RETURN_OK = 0,
    RETURN_ERROR = 1,
}; // ReturnCode

extern const unsigned INVALID_UNSIGNED;
extern const unsigned INVALID_UNSIGNED_SHORT;

} // namespace util

#endif // STDAFX_H

stdafx.cpp

#include "stdafx.h"

namespace util {

const unsigned INVALID_UNSIGNED = static_cast<const unsigned>( -1 );
const unsigned INVALID_UNSIGNED_SHORT = static_cast<const unsigned short>( -1 );

} // namespace util

Utility.h

#ifndef UTILITY_H
#define UTILITY_H

namespace util {

class Utility {
public:

    static void pressAnyKeyToQuit();

    static std::string  toUpper(const std::string& str);
    static std::string  toLower(const std::string& str);
    static std::string  trim(const std::string& str, const std::string elementsToTrim = " \t\n\r");

    static unsigned     convertToUnsigned(const std::string& str);
    static int          convertToInt(const std::string& str);
    static float        convertToFloat(const std::string& str);

    static std::vector<std::string> splitString(const std::string& strStringToSplit, const std::string& strDelimiter, const bool keepEmpty = true);

private:
    Utility(); // Private - Not A Class Object
    Utility(const Utility& c); // Not Implemented
    Utility& operator=(const Utility& c); // Not Implemented

    template<typename T>
    static bool stringToValue(const std::string& str, T* pValue, unsigned uNumValues);

    template<typename T>
    static T getValue(const std::string& str, std::size_t& remainder);

}; // Utility

#include "Utility.inl"

} // namespace util

#endif // UTILITY_H

Utility.inl

// ----------------------------------------------------------------------------
// stringToValue()
template<typename T>
static bool Utility::stringToValue(const std::string& str, T* pValue, unsigned uNumValues) {
    int numCommas = std::count(str.begin(), str.end(), ',');
    if (numCommas != uNumValues - 1) {
        return false;
    }

    std::size_t remainder;
    pValue[0] = getValue<T>(str, remainder);

    if (uNumValues == 1) {
        if (str.size() != remainder) {
            return false;
        }
    }
    else {
        std::size_t offset = remainder;
        if (str.at(offset) != ',') {
            return false;
        }

        unsigned uLastIdx = uNumValues - 1;
        for (unsigned u = 1; u < uNumValues; ++u) {
            pValue[u] = getValue<T>(str.substr(++offset), remainder);
            offset += remainder;
            if ((u < uLastIdx && str.at(offset) != ',') ||
                (u == uLastIdx && offset != str.size()))
            {
                return false;
            }
        }
    }
    return true;
} // stringToValue

Utility.cpp

#include "stdafx.h"
#include "Utility.h"

namespace util {

// ----------------------------------------------------------------------------
// pressAnyKeyToQuit()
void Utility::pressAnyKeyToQuit() {
    std::cout << "Press any key to quit" << std::endl;
    _getch();
} // pressAnyKeyToQuit

// ----------------------------------------------------------------------------
// toUpper()
std::string Utility::toUpper( const std::string& str ) {
    std::string result = str;
    std::transform( str.begin(), str.end(), result.begin(), ::toupper );
    return result;
} // toUpper


// ----------------------------------------------------------------------------
// toLower()
std::string Utility::toLower( const std::string& str ) {
    std::string result = str;
    std::transform( str.begin(), str.end(), result.begin(), ::tolower );
    return result;
} // toLower

// ----------------------------------------------------------------------------
// trim()
// Removes Elements To Trim From Left And Right Side Of The str
std::string Utility::trim( const std::string& str, const std::string elementsToTrim ) {
    std::basic_string<char>::size_type firstIndex = str.find_first_not_of( elementsToTrim );
    if ( firstIndex == std::string::npos ) {
        return std::string(); // Nothing Left
    }

    std::basic_string<char>::size_type lastIndex = str.find_last_not_of( elementsToTrim );
    return str.substr( firstIndex, lastIndex - firstIndex + 1 );
} // trim

// ----------------------------------------------------------------------------
// getValue()
template<>
float Utility::getValue( const std::string& str, std::size_t& remainder ) {
    return std::stof( str, &remainder );
} // getValue <float>

// ----------------------------------------------------------------------------
// getValue()
template<>
int Utility::getValue( const std::string& str, std::size_t& remainder ) {
    return std::stoi( str, &remainder );
} // getValue <int>

// ----------------------------------------------------------------------------
// getValue()
template<>
unsigned Utility::getValue( const std::string& str, std::size_t& remainder ) {
    return std::stoul( str, &remainder );
} // getValue <unsigned>

// ----------------------------------------------------------------------------
// convertToUnsigned()
unsigned Utility::convertToUnsigned( const std::string& str ) {
    unsigned u = 0;
    if ( !stringToValue( str, &u, 1 ) ) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to unsigned";
        throw strStream.str();
    }
    return u;
} // convertToUnsigned

// ----------------------------------------------------------------------------
// convertToInt()
int Utility::convertToInt( const std::string& str ) {
    int i = 0;
    if ( !stringToValue( str, &i, 1 ) ) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to int";
        throw strStream.str();
    }
    return i;
} // convertToInt

// ----------------------------------------------------------------------------
// convertToFloat()
float Utility::convertToFloat(const std::string& str) {
    float f = 0;
    if (!stringToValue(str, &f, 1)) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to float";
        throw strStream.str();
    }
    return f;
} // convertToFloat

// ----------------------------------------------------------------------------
// splitString()
std::vector<std::string> Utility::splitString( const std::string& strStringToSplit, const std::string& strDelimiter, const bool keepEmpty ) {
    std::vector<std::string> vResult;
    if ( strDelimiter.empty() ) {
        vResult.push_back( strStringToSplit );
        return vResult;
    }

    std::string::const_iterator itSubStrStart = strStringToSplit.begin(), itSubStrEnd;
    while ( true ) {
        itSubStrEnd = search( itSubStrStart, strStringToSplit.end(), strDelimiter.begin(), strDelimiter.end() );
        std::string strTemp( itSubStrStart, itSubStrEnd );
        if ( keepEmpty || !strTemp.empty() ) {
            vResult.push_back( strTemp );
        }

        if ( itSubStrEnd == strStringToSplit.end() ) {
            break;
        }

        itSubStrStart = itSubStrEnd + strDelimiter.size();
    }

    return vResult;

} // splitString

} // namspace util

CharacterData.h

#ifndef CHARACTER_DATA_H
#define CHARACTER_DATA_H

class CharacterData {
private:
    std::string m_name;
    float       m_health;
    unsigned    m_exp;

public:
    CharacterData();
    CharacterData( const std::string& name, float health, unsigned exp );   

    void setName( const std::string& name );
    void setHealth( float health );
    void setExperience( unsigned exp );

    std::string getName() const;
    float       getHealth() const;
    unsigned    getExperience() const;

private:
    CharacterData( const CharacterData& c ); // Not Implemented
    CharacterData& operator=( const CharacterData& c ); // Not Implemented      

}; // CharacterData

#endif // CHARACTER_DATA_H

CharacterData.cpp

#include "stdafx.h"
#include "CharacterData.h"

// ----------------------------------------------------------------------------
// CharacterData()
CharacterData::CharacterData() :
m_name( std::string() ),
m_health( 0.0f ),
m_exp( 0 ) {
} // CharacterData

// ----------------------------------------------------------------------------
// CharacterData()
CharacterData::CharacterData( const std::string& name, float health, unsigned exp ) :
m_name( name ),
m_health( health ),
m_exp( exp ) {
} // CharacterData

// ----------------------------------------------------------------------------
// setName()
void CharacterData::setName( const std::string& name ) {
    m_name = name;
} // setName

// ----------------------------------------------------------------------------
// getName()
std::string CharacterData::getName() const {
    return m_name;
} // getName

// ----------------------------------------------------------------------------
// setHealth()
void CharacterData::setHealth( float health ) {
    m_health = health;
} // setHealth

// ----------------------------------------------------------------------------
// getHealth()
float CharacterData::getHealth() const {
    return m_health;
} // getHealth

// ----------------------------------------------------------------------------
// setExperience()
void CharacterData::setExperience( unsigned exp ) {
    m_exp = exp;
} // setExperience

// ----------------------------------------------------------------------------
// getExperience()
unsigned CharacterData::getExperience() const {
    return m_exp;
} // getExperience

CharacterDatabase.h

#ifndef CHARACTER_DATABASE_H
#define CHARACTER_DATABASE_H

class CharacterData;

class CharacterDatabase {
private:
    std::string m_filename;
    std::vector<std::shared_ptr<CharacterData>> m_vpCharacters;

public:
    explicit CharacterDatabase( const std::string& filename );
    // ~CharacterDatabase(); // Default Okay

    void displayByOption( unsigned option = 0 );

private:
    CharacterDatabase( const CharacterDatabase& c ); // Not Implemented
    CharacterDatabase& operator=( const CharacterDatabase& c ); // Not Implemented

    void parseFile();
    void display() const;

    static bool sortByName( const std::shared_ptr<CharacterData>& d1, const std::shared_ptr<CharacterData>& d2);
    static bool sortByHealth( const std::shared_ptr<CharacterData>& d1, const std::shared_ptr<CharacterData>& d2);
    static bool sortByExperience(const std::shared_ptr<CharacterData>& d1, const std::shared_ptr<CharacterData>& d2);

}; // CharacterDataase

#endif // CHARACTER_DATABASE_H

CharacterDatabase.cpp

#include "stdafx.h"
#include "CharacterDatabase.h"

#include "CharacterData.h"
#include "Utility.h"

// ----------------------------------------------------------------------------
// CharacterDatabase()
CharacterDatabase::CharacterDatabase( const std::string& filename ) :
m_filename( filename ) {
    parseFile();
} // CharacterDatabase

// ----------------------------------------------------------------------------
// parseFile()
void CharacterDatabase::parseFile() {
    using namespace util;

    if ( m_filename.empty() ) {
        std::cout << "Missing or invalid filename." << std::endl;
        return;
    }

    std::string line;
    std::vector<std::string> results;
    std::ifstream in;

    // Try To Open File For Reading
    in.open( m_filename.c_str(), std::ios_base::in );
    if ( !in.is_open() ) {
        std::cout << "Can not open file(" << m_filename << ") for reading.";
        return;
    }

    // Read Line By Line And Store Contents Into String & Parse Each Line One At A Time.
    while ( !in.eof() )  {
        std::getline( in, line );
        results = Utility::splitString( line, " " );

        // On Each Pass We Want To Construct A CharacterData Object & Save It Into Our Container
        m_vpCharacters.push_back( std::make_shared<CharacterData>( results[0], Utility::convertToFloat( results[1] ), Utility::convertToUnsigned( results[2] ) ) );
    }

    // Close File Pointer
    in.close();

} // parseFile

// ----------------------------------------------------------------------------
// display()
void CharacterDatabase::display() const {
    for (unsigned u = 0; u < m_vpCharacters.size(); u++) {
        std::cout << m_vpCharacters[u]->getName() << "\t"
            << m_vpCharacters[u]->getHealth() << "\t"
            << m_vpCharacters[u]->getExperience() << std::endl;
    }
    std::cout << std::endl;
} // display

// ----------------------------------------------------------------------------
// displayByOption() Default 0 = Order In Which File Is Read In, 
// 1 = Sorted By Name, 2 = Sorted By Health, 3 = Sorted By Experience
void CharacterDatabase::displayByOption( unsigned option ) {

    switch ( option ) {
        case 1: { // Sorted By Name
            std::sort( m_vpCharacters.begin(), m_vpCharacters.end(), sortByName );
            display();
            break;
        }
        case 2: { // Sorted By Health
            std::sort( m_vpCharacters.begin(), m_vpCharacters.end(), sortByHealth );
            display();
            break;
        }
        case 3: { // Sorted By Experience
            std::sort( m_vpCharacters.begin(), m_vpCharacters.end(), sortByExperience );
            display();
            break;
        }
        default: { // Unsorted - Order Read In By File
            display();
        }
    }

} // displayByOption

// ----------------------------------------------------------------------------
// sortByName()
bool CharacterDatabase::sortByName( const std::shared_ptr<CharacterData>& d1, const std::shared_ptr<CharacterData>& d2 ) {
    return d1->getName() < d2->getName();
} // sortByName

// ----------------------------------------------------------------------------
// sortByHealth()
bool CharacterDatabase::sortByHealth( const std::shared_ptr<CharacterData>& d1, const std::shared_ptr<CharacterData>& d2 ) {
    return d1->getHealth() < d2->getHealth();
} // sortByHealth

// ----------------------------------------------------------------------------
// sortByExperience()
bool CharacterDatabase::sortByExperience( const std::shared_ptr<CharacterData>& d1, const std::shared_ptr<CharacterData>& d2) {
    return d1->getExperience() < d2->getExperience();
} // sortByExperience

characterData.txt

Alice 23.4 3210
Xander 45.3 1110
Bernard 12.9 2024
Yanni 23.7 1098
Craw 50.5 980
Zack 11.9 1024

ma​​in.cpp

#include "stdafx.h"
#include "Utility.h"    
#include "CharacterDatabase.h"

int main() {
    using namespace util;

    CharacterDatabase cd( "characterData.txt" );

    cd.displayByOption();
    cd.displayByOption( 1 );
    cd.displayByOption( 2 );
    cd.displayByOption( 3 );

    Utility::pressAnyKeyToQuit();
    return RETURN_OK;
} // main

只要确保您从正确的路径调用文件,在使用 Utility::splitString() 方法时也要确保第二个参数或分隔符与您存储的文件中的内容相匹配。 splitString() 方法将在每次看到该字符时从文件的每一行中分割字符串。如果您碰巧有多个姓名,例如您希望将名字和姓氏存储到单个字符串中,则需要多加注意。

【讨论】:

  • 哈哈,谢谢弗朗西斯,但你已经超出了我对 C++ XD 的了解。我非常感谢你的帮助,一旦我的课赶上了一些概念
  • @William 没问题。即使您是新手并且您目前的理解有限,我也提出了这一点,因为 70% 的时间在学校教授的内容在实践中并不总是足够的。学习该语言的语法以及该语言的不同方面是需要时间的,但是许多地方未能教授的最大的事情是您的 main.cpp 文件或生成可执行文件的代码应该很简单,紧凑且易于阅读。这里的库只是一个可重用的类。结构体和类非常相似,但也有一些区别。
  • @William (...continued) 只要你在一个至少有 VS2012、13 或 15 的 Windows 平台上,你可以用我介绍的内容做的最好的事情就是创建一个带有预编译标头的新空 win32 控制台解决方案,将这些文件与文本数据文件一起添加到解决方案中。确保适当的包含在标准包含文件中。编译它以确保所有内容都可以编译,然后构建以确保所有内容也都可以构建。然后运行程序,然后在执行的第一行设置一个断点,并逐步执行代码以遵循逻辑。
  • @William (...继续) 如果您使用的是 Mac、Linux/Unix,那么您将不得不针对每个平台进行一些特定的更改。这是使用标志“多字节”而不是 VS 项目设置中的默认“Unicode”构建的。当我第一次开始学习 c++ 时,这个网站还不存在,我不得不求助于非常有限的书籍资源和 90 年代的一些文本网站,你刚刚阅读,如果它不起作用,你就不能'不要问问题。由于我是自学成才的,我喜欢在力所能及的范围内帮助他人,让别人获得我没有的优势。
  • 感谢你的代码帮助了我很多......它甚至教会了我更多,它帮助我完成下一个任务XD
【解决方案3】:

对于基于任何 col 的排序,您可以在 algorithm 标头中使用 sort() 和您自己的比较函数。
像这样定义比较函数:

基于健康:

bool MyCompHealth(struct charData a,struct charData b) 
{ 
    return (a.charHealth>b.charHealth);
}

在Exp的基础上:

bool MyCompExp(struct charData a,struct charData b) 
{ 
    return (a.charExp > b.charExp); 
}

基于名称:

bool MyCompName(struct charData a,struct charData b) 
{ 
    int r = a.charName.compare(b.charName);
    return (r > 0);
}

代码:

#include<algorithm>
#include<iostream>
#include<fstream>
using namespace std;

struct charData
{
    string charName;
    float charHealth;
    int charExp;
};

bool MyCompHealth(struct charData a,struct charData b) 
{ 
    return (a.charHealth<b.charHealth);
}

//on the basis of Exp:

bool MyCompExp(struct charData a,struct charData b) 
{ 
    return (a.charExp < b.charExp); 
}

//on the basis of Name:

bool MyCompName(struct charData a,struct charData b) 
{ 
    int r = a.charName.compare(b.charName);
    return !(r > 0);
}

int main() {
    const int NUM_COLS = 3;
    int NUM_ROWS = 6;
    int charNumber = 0;
    int userInput;
    int loop = 0;
    int i,j;
    string line;

    ifstream myIn;    // Declare read file stream
    myIn.open("party.dat",ios::in); // Open file    

    struct charData charArray[NUM_ROWS];

    while( !myIn.eof() )   //taking input
    {
        for ( i = 0; i <  NUM_ROWS ; i++) 
        {
             myIn >> charArray[i].charName;    
             myIn >> charArray[i].charHealth;
             myIn >> charArray[i].charExp;       
        }    
    }
    cout<<"How you want to sort?(1:health 2:Exp 3:Name)\n";
    cin>>userInput; //input from user how he wants to sort

    if(userInput==1)
    {
        //sorting on the basis of health
        sort(charArray,charArray+NUM_ROWS,MyCompHealth);
    }
    else if(userInput==1)
    {
        //sorting on the basis of Experience
        sort(charArray,charArray+NUM_ROWS,MyCompExp);
    }
    else
    {
        //sorting on the basis of Name
        sort(charArray,charArray+NUM_ROWS,MyCompName);
    }

    //display result

        for ( i = 0; i <  NUM_ROWS ; i++) 
        {
             cout<<charArray[i].charName<<" ";    
             cout<<charArray[i].charHealth<<" ";
             cout<<charArray[i].charExp<<endl;       
        }    

    return 0;    
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-03
    • 1970-01-01
    • 2020-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-28
    相关资源
    最近更新 更多