C++ 如何有效地将大std::string的一部分转换为float?

C++ 如何有效地将大std::string的一部分转换为float?,c++,parsing,wavefront,C++,Parsing,Wavefront,这是我用来将缓冲区字符串中的文本转换为float并将其放入向量中的代码。字符串的重要部分本身具有以下结构: void OBJLoader::load_vec(const char* line_prefix) { size_t jump = 0; size_t strpos = 0; if (line_prefix == "v ") { while ((strpos = buffer.find(line_prefix, strpos)) != string::npos) {

这是我用来将缓冲区字符串中的文本转换为float并将其放入向量中的代码。字符串的重要部分本身具有以下结构:

void OBJLoader::load_vec(const char* line_prefix) {
size_t jump = 0;
size_t strpos = 0;
if (line_prefix == "v ") {
    while ((strpos = buffer.find(line_prefix, strpos)) != string::npos) {
        strpos++;
        vcoord_vec.emplace_back(stof(&buffer.at(strpos), &jump));
        strpos += jump;
        vcoord_vec.emplace_back(stof(&buffer.at(strpos), &jump));
        strpos += jump;
        vcoord_vec.emplace_back(stof(&buffer.at(strpos), &jump));
        strpos += jump;
    }
    return;
}
//(...)
使用vcoord_vec.emplace_backstof&buffer.atstrpos和jump,将单个值加载到向量中大约需要200毫秒,而这:

v -0.893339 0.784809 0.891470
v -0.893339 -0.784809 0.891470
v -0.692655 -0.634043 0.017402
v 0.692655 0.586786 -0.017402
v -0.710057 0.651445 0.000000
v 0.710057 -0.604188 0.000000
v 0.017402 -0.571364 -0.674429
v -0.017402 0.618621 0.674429
v 0.000000 -0.636023 0.691831

在489ms内加载其中的4718598个。什么是C++方法将字符串中已知的一部分转换成任何类型的数字,具有类似的效率?

< P>对于像你所使用的具有空间分隔值的真正基本格式,可以使用C++流:

包括 /*...*/ 字符id; 浮动v[3]; 标准::istringstream issv 123.4 12.5 0262.2; iss>>id; 如果id=='v'{ iss>>v[0]>>v[1]>>v[2]; } 您使用std::stof时出错。当它需要一个常量std::string&时,您正在向它传递一个char*

其结果是在每次调用std::stof时,从char*隐式转换为std::string,将从strpos开始的缓冲区的全部内容复制到其空终止符,并将其复制到通过转换函数参数创建的临时值中


如果C++17可用,则更喜欢std::from_chars,否则我建议使用std::strof进行转换,这并不意味着您必须继续使用strstr。std::stof被指定为无论如何都要调用std::strtof。

这里有一个我在3D图形引擎中使用的实用程序类。对于我的示例,您需要有一个可用的GLM库版本,否则您可以注释掉所有与GLM相关的内容,以完成单个值的简单基本转换。但是,我在这里演示如何将字符串转换为不同的数字类型,包括用户定义的类型

无法构造此实用程序类对象的实例,因为它的构造函数已删除,并且没有成员变量。它只是一个类似或相关函数的集合,主要是字符串操作方法

如果您花时间通读这个类,您将看到我正在使用静态函数将字符串转换为所需的类型


该类确实有2个私有函数模板,string_to_value(在类外部的头中定义)和get_value(在cpp文件中定义),因为有3个基于返回类型的重载函数或模板专门化函数。它们分别是int、unsigned和float。我还包括了重载运算符。如果有可用的话,您应该使用std::from_chars。200ms将单个值加载到向量中:stof可能很慢,但这似乎太过分了。你确定这不是打字错误吗?您是否在第_prefix==“v”行上进行了优化编译“这可能适用于某些编译器,但您应该避免。标准并没有规定相等的字符串文字必须具有相等的地址。@我是肯定的,正在进行优化,感觉每次调用都要复制250MB的字符串。实际上visual studio中的内存监视器在使用中显示出奇怪的峰值。@BigTemp What type是缓冲区?您是否尝试过将.at…替换为带[…]的绑定检查标准容器/字符串,而[…]没有?
void OBJLoader::load_vec(const char* line_prefix) {
    char* cbuffer = const_cast<char*>(buffer.c_str());
    if (line_prefix == "v ") {
        while (cbuffer = strstr(cbuffer, line_prefix)) {
            cbuffer++;
            vcoord_vec.emplace_back(strtof(cbuffer, &cbuffer));
            vcoord_vec.emplace_back(strtof(cbuffer, &cbuffer));
            vcoord_vec.emplace_back(strtof(cbuffer, &cbuffer));
        }
        return;
    }
//(...)
#include <iostream>
#include "Utility.h"

int main() {
    using namespace util;

    try {
        std::string str1("3,4,5");
        std::string str2("-4,-2,7");
        std::string str3("2.4,7.8,9.2");      

        glm::uvec3 uvec3 = Utility::convert_to_uvec3(str1);
        glm::ivec3 ivec3 = Utility::convert_to_ivec3(str2);
        glm::vec3   vec3 = Utility::convert_to_vec3(str3);

        std::cout << uvec3 << '\n';
        std::cout << ivec3 << '\n';       
        std::cout <<  vec3 << '\n';

        // test an exception case:
        glm::vec4 v4 = Utility::convert_to_vec4(str2);

    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}
(3,4,5)
(-4,-2,7)
(2.4,7.8,9.2)
util::Utility::convert_to_vec4 Bad conversion of [-4,-2,7] to vec4
#ifndef UTILITY_H
#define UTILITY_H

#include <string>
#include <algorithm>
#include <glm/glm.hpp>

namespace util {

    class Utility {
    public:
        static std::string to_upper(const std::string& str);
        static std::string to_lower(const std::string& str);
        static std::string trim(const std::string& str, const std::string elements_to_trim = " \t\n\r");

        static unsigned     convert_to_unsigned(const std::string& str);
        static int          convert_to_int(const std::string& str);
        static float        convert_to_float(const std::string& str);

        static glm::vec2    convert_to_vec2(const std::string& str);
        static glm::vec3    convert_to_vec3(const std::string& str);
        static glm::vec4    convert_to_vec4(const std::string& str);

        static glm::ivec2   convert_to_ivec2(const std::string& str);
        static glm::ivec3   convert_to_ivec3(const std::string& str);
        static glm::ivec4   convert_to_ivec4(const std::string& str);

        static glm::uvec2   convert_to_uvec2(const std::string& str);
        static glm::uvec3   convert_to_uvec3(const std::string& str);
        static glm::uvec4   convert_to_uvec4(const std::string& str);

    private:
        Utility() = delete;
        Utility(const Utility& c) = delete;
        Utility& operator=(const Utility& c) = delete;

        template<typename T>
        static bool string_to_value(const std::string& str, T* value, unsigned num_values);

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

    template<typename T>
    static bool Utility::string_to_value(const std::string& str, T* value, unsigned num_values) {
        int num_commas = std::count(str.begin(), str.end(), ',');
        if (num_commas != num_values - 1) return false;

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

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

            unsigned last_indx = num_values - 1;
            for (unsigned u = 1; u < num_values; ++u) {
                value[u] = get_value<T>(str.substr(++offset), remainder);
                offset += remainder;
                if ((u < last_indx && str.at(offset) != ',') ||
                    (u == last_indx && offset != str.size())) {
                    return false;
                }
            }
        }
        return true;
    }

} // namespace util

std::ostream& operator<<(std::ostream& out, const glm::ivec2& v2Value);
std::ostream& operator<<(std::ostream& out, const glm::ivec3& v3Value);
std::ostream& operator<<(std::ostream& out, const glm::ivec4& v4Value);

std::ostream& operator<<(std::ostream& out, const glm::vec2& v2Value);
std::ostream& operator<<(std::ostream& out, const glm::vec3& v3Value);
std::ostream& operator<<(std::ostream& out, const glm::vec4& v4Value);

std::ostream& operator<<(std::ostream& out, const glm::uvec2& v2Value);
std::ostream& operator<<(std::ostream& out, const glm::uvec3& v3Value);
std::ostream& operator<<(std::ostream& out, const glm::uvec4& v4Value);

#endif // UTILITY_H
#include "Utility.h"

#include <exception>
#include <sstream>

namespace util {
    std::string Utility::to_upper(const std::string& str) {
        std::string result = str;
        std::transform(str.begin(), str.end(), result.begin(), ::toupper);
        return result;
    }

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

    std::string Utility::trim(const std::string& str, const std::string elements_to_trim) {
        std::basic_string<char>::size_type first_index = str.find_first_not_of(elements_to_trim);
        if (first_index == std::string::npos) {
            return std::string(); // nothing left
        }

        std::basic_string<char>::size_type last_index = str.find_last_not_of(elements_to_trim);
        return str.substr(first_index, last_index - first_index + 1);
    }

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

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

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

    unsigned Utility::convert_to_unsigned(const std::string& str) {
        unsigned u = 0;
        if (!string_to_value(str, &u, 1)) {
            std::ostringstream stream;
            // __FUNCTION__ works on windows otherwise user can exchange - modify to use __PRETTY_FUNCTION__
            stream << __FUNCTION__ << " Bad conversion of [" << str << "] to unsigned";
            throw std::exception(stream.str().c_str());
        }
        return u;
    }

    int Utility::convert_to_int(const std::string& str) {
        int i = 0;
        if (!string_to_value(str, &i, 1)) {
            std::ostringstream stream;
            // __FUNCTION__ works on windows otherwise user can exchange - modify to use __PRETTY_FUNCTION__
            stream << __FUNCTION__ << " Bad conversion of [" << str << "] to int";
            throw std::exception(stream.str().c_str());
        }
        return i;
    }

    float Utility::convert_to_float(const std::string& str) {
        float f = 0;
        if (!string_to_value(str, &f, 1)) {
            std::ostringstream stream;
            // __FUNCTION__ works on windows otherwise user can exchange - modify to use __PRETTY_FUNCTION__
            stream << __FUNCTION__ << " Bad conversion of [" << str << "] to float";
            throw std::exception(stream.str().c_str());
        }
        return f;
    }

    glm::vec2 Utility::convert_to_vec2(const std::string& str) {
        glm::vec2 v2;
        if (!string_to_value(str, &v2[0], 2)) {
            std::ostringstream stream;
            // __FUNCTION__ works on windows otherwise user can exchange - modify to use __PRETTY_FUNCTION__
            stream << __FUNCTION__ << " Bad conversion of [" << str << "] to vec2";
            throw std::exception(stream.str().c_str());
        }
        return v2;
    }

    glm::vec3 Utility::convert_to_vec3(const std::string& str) {
        glm::vec3 v3;
        if (!string_to_value(str, &v3[0], 3)) {
            std::ostringstream stream;
            // __FUNCTION__ works on windows otherwise user can exchange - modify to use __PRETTY_FUNCTION__
            stream << __FUNCTION__ << " Bad conversion of [" << str << "] to vec3";
            throw std::exception(stream.str().c_str());
        }
        return v3;
    }

    glm::vec4 Utility::convert_to_vec4(const std::string& str) {
        glm::vec4 v4;
        if (!string_to_value(str, &v4[0], 4)) {
            std::ostringstream stream;
            // __FUNCTION__ works on windows otherwise user can exchange - modify to use __PRETTY_FUNCTION__
            stream << __FUNCTION__ << " Bad conversion of [" << str << "] to vec4";
            throw std::exception(stream.str().c_str());
        }
        return v4;
    }

    glm::ivec2 Utility::convert_to_ivec2(const std::string& str) {
        glm::ivec2 v2;
        if (!string_to_value(str, &v2[0], 2)) {
            std::ostringstream stream;
            // __FUNCTION__ works on windows otherwise user can exchange - modify to use __PRETTY_FUNCTION__
            stream << __FUNCTION__ << " Bad conversion of [" << str << "] to ivec2";
            throw std::exception(stream.str().c_str());
        }
        return v2;
    }

    glm::ivec3 Utility::convert_to_ivec3(const std::string& str) {
        glm::ivec3 v3;
        if (!string_to_value(str, &v3[0], 3)) {
            std::ostringstream stream;
            // __FUNCTION__ works on windows otherwise user can exchange - modify to use __PRETTY_FUNCTION__
            stream << __FUNCTION__ << " Bad conversion of [" << str << "] to ivec3";
            throw std::exception(stream.str().c_str());
        }
        return v3;
    }

    glm::ivec4 Utility::convert_to_ivec4(const std::string& str) {
        glm::ivec4 v4;
        if (!string_to_value(str, &v4[0], 4)) {
            std::ostringstream stream;
            // __FUNCTION__ works on windows otherwise user can exchange - modify to use __PRETTY_FUNCTION__
            stream << __FUNCTION__ << " Bad conversion of [" << str << "] to ivec4";
            throw std::exception(stream.str().c_str());
        }
        return v4;
    }

    glm::uvec2 Utility::convert_to_uvec2(const std::string& str) {
        glm::uvec2 v2;
        if (!string_to_value(str, &v2[0], 2)) {
            std::ostringstream stream;
            // __FUNCTION__ works on windows otherwise user can exchange - modify to use __PRETTY_FUNCTION__
            stream << __FUNCTION__ << " Bad conversion of [" << str << "] to uvec2";
            throw std::exception(stream.str().c_str());
        }
        return v2;
    }

    glm::uvec3 Utility::convert_to_uvec3(const std::string& str) {
        glm::uvec3 v3;
        if (!string_to_value(str, &v3[0], 3)) {
            std::ostringstream stream;
            // __FUNCTION__ works on windows otherwise user can exchange - modify to use __PRETTY_FUNCTION__
            stream << __FUNCTION__ << " Bad conversion of [" << str << "] to uvec3";
            throw std::exception(stream.str().c_str());
        }
        return v3;
    }

    glm::uvec4 Utility::convert_to_uvec4(const std::string& str) {
        glm::uvec4 v4;
        if (!string_to_value(str, &v4[0], 4)) {
            std::ostringstream stream;
            // __FUNCTION__ works on windows otherwise user can exchange - modify to use __PRETTY_FUNCTION__
            stream << __FUNCTION__ << " Bad conversion of [" << str << "] to uvec4";
            throw std::exception(stream.str().c_str());
        }
        return v4;
    }

} // namespace util

std::ostream& operator<<(std::ostream& out, const glm::ivec2& v2Value) {
    out << "("
        << v2Value.x << ","
        << v2Value.y << ")";
    return out;
}

std::ostream& operator<<(std::ostream& out, const glm::ivec3& v3Value) {
    out << "("
        << v3Value.x << ","
        << v3Value.y << ","
        << v3Value.z << ")";
    return out;
}

std::ostream& operator<<(std::ostream& out, const glm::ivec4& v4Value) {
    out << "("
        << v4Value.x << ","
        << v4Value.y << ","
        << v4Value.z << ","
        << v4Value.w << ")";
    return out;
}

std::ostream& operator<<(std::ostream& out, const glm::vec2& v2Value) {
    out << "("
        << v2Value.x << ","
        << v2Value.y << ")";
    return out;
}

std::ostream& operator<<(std::ostream& out, const glm::vec3& v3Value) {
    out << "("
        << v3Value.x << ","
        << v3Value.y << ","
        << v3Value.z << ")";
    return out;
}

std::ostream& operator<<(std::ostream& out, const glm::vec4& v4Value) {
    out << "("
        << v4Value.x << ","
        << v4Value.y << ","
        << v4Value.z << ","
        << v4Value.w << ")";
    return out;
}

std::ostream& operator<<(std::ostream& out, const glm::uvec2& v2Value) {
    out << "("
        << v2Value.x << ","
        << v2Value.y << ")";
    return out;
} 

std::ostream& operator<<(std::ostream& out, const glm::uvec3& v3Value) {
    out << "("
        << v3Value.x << ","
        << v3Value.y << ","
        << v3Value.z << ")";
    return out;
} 

std::ostream& operator<<(std::ostream& out, const glm::uvec4& v4Value) {
    out << "("
        << v4Value.x << ","
        << v4Value.y << ","
        << v4Value.z << ","
        << v4Value.w << ")";
    return out;
}