简体   繁体   English

如何在C ++中从一串科学数字中修剪零

[英]How to trim following zeros from a string of scientific number in c++

I am trying to trim zeros from strings in scientific number form: 我正在尝试以科学数字形式从字符串中修剪零:

  1. 1.200e+12 should be 1.2e+12 1.200e+12应该是1.2e+12
  2. 1.0200e+12 should be 1.02e+12 1.0200e+12应该是1.02e+12
  3. 1.0000e+12 should be 1e+12 1.0000e+12应该是1e+12
  4. 1.0e+12 should be 1e+12 1.0e+12应该是1e+12
  5. 100e+12 should be 100e+12 100e+12应该是100e+12
  6. 1.e+12 should be 1e+12 (optional) 1.e+12应该是1e+12 (可选)

I am using this function to do this: 我正在使用此功能来做到这一点:

std::regex_replace(s,std::regex("([^\\.]+(?=\\.)|[^\\.]+\\.\\d+?)0*(e.*)$"),\
                            "$1$2",std::regex_constants::format_first_only)

But the regex seems to be not correct. 但是正则表达式似乎是不正确的。 It gives me wrong result: 它给我错误的结果:

  1. 1.0e for 1.000e 1.0e1.000e
  2. 1.0e for 1.0e 1.0e1.0e
  3. 1.e for 1.e 1.e代表1.e

How can I do this with regex or is there another efficient way without using regex at all? 我该如何使用正则表达式来执行此操作,或者还有一种根本不用正则表达式的有效方法?

I would use a conditional regex like this: 我将使用这样的条件正则表达式:

(\d*)(?:(?=[.]0*e)[.]0*|(?![.]0+e)([.][0-9]*?)0*)(e(?:[+]\d+)?)

See regex demo . 参见regex演示 Replace with $1$2$3 . 替换为$1$2$3

The regex matches... 正则表达式匹配...

  • (\\d*) - (Group 1) 0 or more digits (\\d*) -(第1组)0或更多数字
  • (?:(?=[.]0*e)[.]0*|(?![.]0+e)([.][0-9]*?)0*) - two alternatives: (?:(?=[.]0*e)[.]0*|(?![.]0+e)([.][0-9]*?)0*) -两种选择:
    • (?=[.]0*e)[.]0* - match 0 or more zeros if these are 0s coming after a dot up to e (?=[.]0*e)[.]0* -匹配0个或多个零(如果它们是0,则在点之后直到e
    • | - or... - 要么...
    • (?![.]0+e)([.][0-9]*?)0* - match and capture into Group 2 a dot with as few digits as possible before 0 or more 0 s if there are no just zeros after . (?![.]0+e)([.][0-9]*?)0* - 如果没有正表达式, 则将第一个点匹配并捕获到第2组中,该点应在0或多个0之前尽可能少之后为零. up to e 直到e
  • (e(?:[+]\\d+)?) - (Group 3) match and capture e or e+ followed with 1 or more digits. (e(?:[+]\\d+)?) -(第3组)匹配并捕获ee+后跟1个或多个数字。

IDEONE demo : IDEONE演示

#include <iostream>
#include <regex>
using namespace std;

int main() {
    std::vector<std::string> strings;
    strings.push_back("1.200e+12");
    strings.push_back("1.0200e+12");
    strings.push_back("1.0000e+12");
    strings.push_back("1.0e+12");
    strings.push_back("1.e+12");
    strings.push_back("100e");
    strings.push_back("100e+12");
    std::regex reg(R"((\d*)(?:(?=[.]0*e)[.]0*|(?![.]0+e)([.][0-9]*?)0*)(e(?:[+]\d+)?))");
    for (size_t k = 0; k < strings.size(); k++)
    {
        std::cout << "Next string: " << strings[k] << std::endl;
        std::cout << "Replace result: " 
                     << std::regex_replace(strings[k], reg, "$1$2$3") << std::endl;
    }
    return 0;
}

I'd suggest that you proceed with 2 steps: 我建议您进行2个步骤:

  1. remove trailing 0 s: https://regex101.com/r/aV0sM6/1 删除尾随的0秒: https : //regex101.com/r/aV0sM6/1

     RegEx: s/([0-9]+)\\.([0-9]*[^0])?(0*)[eE]([-+][0-9]+)/\\1.\\2e\\4/ Input: Output: 1.200e+12 1.2e+12 1.0200e+12 1.02e+12 1.0000e+12 1.e+12 1.0e+12 1.e+12 1.e+12 1.e+12 
  2. remove trailing . 删除尾随. s: https://regex101.com/r/zT3wI2/1 s: https//regex101.com/r/zT3wI2/1

     s/([0-9]+)\\.[eE]([-+][0-9]+)/\\1e\\2/ Input: Output: 1.2e+12 1.2e+12 1.02e+12 1.02e+12 1.e+12 1e+12 1.e+12 1e+12 1.e+12 1e+12 

EDIT Here's another solution that doesn't use regular expressions: 编辑这是另一种不使用正则表达式的解决方案:

Run It Online 在线运行

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

string trim(string str)
{
    // return integers without modification
    if (str.find('.') == string::npos)
    {
        return str;
    }

    // process floating point numbers

    // find the exponent symbol
    const size_t pos_e  = str.rfind('e');
    const size_t rpos_e = str.length() - pos_e;

    // find the first trailing zero (if any)
    const size_t rpos_firstTrailingZero = std::distance(
        str.rbegin(),
        std::find_if(str.rbegin() + rpos_e,
                     str.rend(),
                     [](const char c) { return c != '0'; })
    );
    const size_t pos_firstTrailingZero = str.length()
                                       - rpos_firstTrailingZero;

    // compute the trimming position
    // if the fractional part was only zeros (e.g. 1.000), then trailing '.' should be removed
    const size_t pos_trimming = (str[pos_firstTrailingZero - 1] != '.')
                              ? pos_firstTrailingZero
                              : (pos_firstTrailingZero - 1);

    // copy the exponent part to where the string should be trimmed
    const size_t len_exp = str.length() - pos_e;  // length of the exponentiation part
    std::copy(str.begin() + pos_e,
              str.end(),
              str.begin() + pos_trimming);
    str.resize(pos_trimming + len_exp);

    // done
    return str;
}

int main()
{
    cout << trim("1.200e+12")  << endl;
    cout << trim("1.0200e+12") << endl;
    cout << trim("1.0000e+12") << endl;
    cout << trim("1.0e+12")    << endl;
    cout << trim("1.e+12")     << endl;
    cout << trim("100e")       << endl;
    cout << trim("100e+12")    << endl;
}

Output: 输出:

1.2e+12
1.02e+12
1e+12
1e+12
1e+12
100e
100e+12

This is what I could think of, and it works for all your given cases. 这就是我能想到的,它适用于所有给定的情况。

string solve(string x)
{
    string y;
    auto d=x.find('.');
    if (d==string::npos) return x;
    for (int i=0; i!=d; i++) y.push_back(x[i]);
    int j, k;
    for (k=d+1; tolower(x[k])!='e'; k++);
    for (j=k-1; x[j]=='0'; j--);
    if (j==d) j--;
    for (int i=d; i<=j; i++) y.push_back(x[i]);
    for (int i=k; i<x.length(); i++) y.push_back(x[i]);
    return y;
}

You could use a stack. 您可以使用堆栈。 For example: 例如:

  • Push each char in the string onto a stack until you reach the 'e'. 将字符串中的每个字符推入堆栈,直到到达'e'。
  • Pop each char off of the stack, stopping when you reach the first non-zero char. 从堆栈中弹出每个字符,并在到达第一个非零字符时停止。
  • Put this (unless it is a decimal) and the remaining chars on the stack into a new string. 将其(除非它是一个十进制数)和堆栈上剩余的字符放入一个新字符串中。
  • append the remaining characters of the original string (after and including the 'e') onto the new string. 将原始字符串的其余字符(在“ e”之后并包括“ e”)附加到新字符串上。

The regex function is too heavy. 正则表达式功能太重。 It takes too much time comparing to a straight forward process. 与简单的过程相比,它花费了太多时间。 I ended up doing this without regex: 我最终没有使用正则表达式来执行此操作:

typedef std::string String;

bool stringContains(String s1,String s2){
    if (s1.find(s2) != String::npos) {return true;}
    else return false;}

String ltrim(String s,const String& delim){return s.erase(0,s.find_first_not_of(delim));}
String rtrim(String s,String delim){return s.erase(s.find_last_not_of(delim)+1);}

String trimZeroFromScientificNumber(const String& s){
    if(!stringContains(s,"."))return s;
    int pos = s.find_first_of("eE");
    if(pos!=(int)String::npos){
        return rtrim(rtrim(s.substr(0,pos),"0"),".")+s.substr(pos,1)+ltrim(s.substr(pos+1,String::npos),"0");
    }
    else return s;
}

Using this function it saved 0.2 seconds in 14 consecutive execution comparing to that regex function in a test run. 与测试运行中的regex函数相比,使用此函数可以在14次连续执行中节省0.2秒。

It can also easily be expanded to trim zeros from non-scientific numbers ( 1.00,1.00200 etc..). 还可以轻松地将其扩展为从非科​​学数字(1.00、1.00200等)中修剪零。

To get a function that can trim floating zeros from both scientific and non-scientific numbers, just change the else return s; 要获得一个可以从科学数和非科学数中修剪掉浮零的函数,只需更改else return s; (in the last line) to else return rtrim(rtrim(s,"0"),"."); (在最后一行), else return rtrim(rtrim(s,"0"),".");

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM