如何将大std :: string的一部分有效地转换为浮点数?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何将大std :: string的一部分有效地转换为浮点数?相关的知识,希望对你有一定的参考价值。

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;
}
//(...)

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

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

使用vcoord_vec.emplace_back(stof(&buffer.at(strpos), &jump))时,将单个值加载到向量中大约需要200毫秒:

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;
    }
//(...)

在489毫秒内加载其中的4 718 598。将具有已知索引的字符串的一部分转换为具有可比效率的数字类型的“ C ++方法”是什么?

答案

您使用的是std::stof错误。您期望char*时将其传递给const std::string&

结果是在每次对char*的调用中从std::stringstd::stof的隐式转换,将buffer开头的strpos的全部内容复制到其空终止符到由C0创建的临时内容中。函数参数的转换。

如果有(C ++ 17),请首选std::from_chars,否则我建议坚持使用std::strtof进行转换(这并不意味着您必须继续使用strstr)。已指定std::stof仍要调用std::strtof

另一答案

对于像您正在使用的使用空格分隔的值的真正基本格式,可以使用C ++流:

#include <sstream>

/*...*/

char id; 
float v[3];

std::istringstream iss("v 123.4 12.5 0262.2");
iss >> id;
if (id == 'v') {
    iss >> v[0] >> v[1] >> v[2];
}
另一答案

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

您无法构造此Utility类对象的实例,因为它的构造函数被删除并且没有成员变量。它只是相似或相关功能的集合,大部分是string操作方法。

如果花时间阅读该类,您会发现我正在使用静态函数将字符串从字符串转换为所需的类型。

该类确实有2个私有函数模板,string_to_value在类的头中定义,get_value在cpp文件中定义,因为根据返回类型有3个重载或template specialization函数。它们分别是intunsignedfloat。我还为所有glm矢量类型包括了重载的operator<<,以便于打印。

我使用get_valuestd::stofstd::stoistd::stoul函数中。如果您想添加转换为double的功能,将其添加到此类中并不难。

我的班级比这大得多,但是为了简短起见,我至少省略了一半。


现在输入代码...

应用程序和输出

main.cpp

#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 << '
';
        std::cout << ivec3 << '
';       
        std::cout <<  vec3 << '
';

        // 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

Utility Class

Utility.h

#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 = " 	

");

        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

Utility.cpp

#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) {
        unsi

以上是关于如何将大std :: string的一部分有效地转换为浮点数?的主要内容,如果未能解决你的问题,请参考以下文章

如何有效地将大字符串从 Python 传递到 C++ 扩展方法?

如何从 std::string 中删除 +

如何检查指向C ++中有效地址的std :: next(x)?

在内存中操作 std::strings 的 2D 动态数组的最有效方法是啥?

如何将部分字符数组附加到字符串?

将文件读入std :: string的最有效方法是什么?