programing

쉼표, 구분 기호가 있는 문자열을 숫자로 구문 분석하려면 어떻게 해야 합니까?

testmans 2023. 10. 2. 11:15
반응형

쉼표, 구분 기호가 있는 문자열을 숫자로 구문 분석하려면 어떻게 해야 합니까?

있습니다2,299.00문자열로, 숫자로 파싱하려고 합니다.사용해 보았습니다.parseFloat, 결과적으로 2개가 됩니다.쉼표가 문제인 것 같은데 어떻게 하면 이 문제를 올바르게 해결할 수 있을까요?그냥 쉼표만 제거해요?

var x = parseFloat("2,299.00")
console.log(x);

예, 쉼표를 제거합니다.

let output = parseFloat("2,299.00".replace(/,/g, ''));
console.log(output);

쉼표를 제거하는 것은 잠재적으로 위험합니다. 왜냐하면 다른 사람들이 주석에서 언급한 것처럼 많은 로컬에서 쉼표를 사용하여 (소수점 자리와 같이) 다른 것을 의미하기 때문입니다.

"2,299.00"=2.299

Intl는 이 할 수 이 될 수 만,지할 수 .Intl.NumberFormat.format() 및 API및 noparse대 :(

문화적 숫자 문자가 포함된 문자열을 i18n의 정상적인 방법으로 기계가 인식할 수 있는 숫자로 구문 분석하는 유일한 방법은 CLDR 데이터를 활용하여 숫자 문자열을 포맷하는 모든 가능한 방법을 차단하는 라이브러리를 사용하는 것입니다. http://cldr.unicode.org/

이를 위해 지금까지 발견한 JS 최고의 두 가지 옵션은 다음과 같습니다.

현대 브라우저에서는 내장된 Intl을 사용할 수 있습니다.숫자 형식 - 브라우저의 숫자 형식을 탐지하고 일치하도록 입력을 정규화합니다.

function parseNumber(value, locales = navigator.languages) {
  const example = Intl.NumberFormat(locales).format('1.1');
  const cleanPattern = new RegExp(`[^-+0-9${ example.charAt( 1 ) }]`, 'g');
  const cleaned = value.replace(cleanPattern, '');
  const normalized = cleaned.replace(example.charAt(1), '.');

  return parseFloat(normalized);
}

const corpus = {
  '1.123': {
    expected: 1.123,
    locale: 'en-US'
  },
  '1,123': {
    expected: 1123,
    locale: 'en-US'
  },
  '2.123': {
    expected: 2123,
    locale: 'fr-FR'
  },
  '2,123': {
    expected: 2.123,
    locale: 'fr-FR'
  },
}


for (const candidate in corpus) {
  const {
    locale,
    expected
  } = corpus[candidate];
  const parsed = parseNumber(candidate, locale);

  console.log(`${ candidate } in ${ corpus[ candidate ].locale } == ${ expected }? ${ parsed === expected }`);
}

최적화 및 캐싱을 수행할 수 있는 여지는 분명하지만, 이는 모든 언어에서 안정적으로 작동합니다.

: : ).1e3천)을 위하여

기호, 제거합니다, 합니다()- ) (a 으로)+이 단 하나의 일을 .+번호로)

만약 당신이 그것을 추정할 수 있다면,. 기호.)로다)와 일 수 있습니다. 계속 읽으십시오).

function convertToFloat(str) {
    let body = str;
    let sign = "";
    const signMatch = /^\s*(-|\+)/.exec(str);
    // Or if you don't want to support unary +:
    // const signMatch = /^\s*(-)/.exec(str);
    if (signMatch) {
        body = str.substring(signMatch.index + 1);
        sign = signMatch[1];
    }
    const updatedBody = str.replace(/[^\d\.]/g, "");
    const num = parseFloat(sign + updatedBody);
    return num;
}

라이브 예제(저는 단지 작동하는 것을 보여주기 위해 숫자에 부분적인 부분을 추가했습니다):

function convertToFloat(str) {
    let body = str;
    let sign = "";
    const signMatch = /^\s*(-|\+)/.exec(str);
    // Or if you don't want to support unary +:
    // const signMatch = /^\s*(-)/.exec(str);
    if (signMatch) {
        body = str.substring(signMatch.index + 1);
        sign = signMatch[1];
    }
    const updatedBody = str.replace(/[^\d\.]/g, "");
    const num = parseFloat(sign + updatedBody);
    return num;
}

console.log(convertToFloat("2,299.23"));

.십진 구분자가 아닙니다(많습니다), 십진 구분자를 탐지하고 탐지된 구분자를 정규식에 사용할 수 있습니다.소수점 구분자 찾기 기능의 예는 다음과 같습니다.

function findDecimalSeparator() {
    const num = 1.2;
    if (typeof Intl === "object" && Intl && Intl.NumberFormat) {
        // I'm surprised it's this much of a pain and am hoping I'm missing
        // something in the API
        const formatter = new Intl.NumberFormat();
        const parts = formatter.formatToParts(num);
        const decimal = parts.find(({ type }) => type === "decimal").value;
        return decimal;
    }
    // Doesn't support `Intl.NumberFormat`, fall back to dodgy means
    const str = num.toLocaleString();
    const parts = /1(\D+)2/.exec(str);
    return parts[1];
}

그리고나서convertToFloat다음과 같습니다.

const decimal = findDecimalSeparator();
function convertToFloat(str) {
    let body = str;
    let sign = "";
    const signMatch = /^\s*(-|\+)/.exec(str);
    // Or if you don't want to support unary +:
    // const signMatch = /^\s*(-)/.exec(str);
    if (signMatch) {
        body = str.substring(signMatch.index + 1);
        sign = signMatch[1];
    }
    const rex = new RegExp(`${escapeRegex(decimal)}|-|\\+|\\D`, "g");
    const updatedBody = body.replace(
        rex,
        (match) => match === decimal ? "." : ""
    );
    const num = parseFloat(sign + updatedBody);
    return num;
}

라이브 예제:

const decimal = findDecimalSeparator();

function findDecimalSeparator() {
    const num = 1.2;
    if (typeof Intl === "object" && Intl && Intl.NumberFormat) {
        // I'm surprised it's this much of a pain and am hoping I'm missing
        // something in the API
        const formatter = new Intl.NumberFormat();
        const parts = formatter.formatToParts(num);
        const decimal = parts.find(({ type }) => type === "decimal").value;
        return decimal;
    }
    // Doesn't support `Intl.NumberFormat`, fall back to dodgy means
    const str = num.toLocaleString();
    const parts = /1(\D+)2/.exec(str);
    return parts[1];
}

function escapeRegex(string) {
    return string.replace(/[/\-\\^$*+?.()|[\]{}]/g, "\\$&");
}

function convertToFloat(str) {
    let body = str;
    let sign = "";
    const signMatch = /^\s*(-|\+)/.exec(str);
    // Or if you don't want to support unary +:
    // const signMatch = /^\s*(-)/.exec(str);
    if (signMatch) {
        body = str.substring(signMatch.index + 1);
        sign = signMatch[1];
    }
    const rex = new RegExp(`${escapeRegex(decimal)}|-|\\+|\\D`, "g");
    const updatedBody = body.replace(
        rex,
        (match) => match === decimal ? "." : ""
    );
    const num = parseFloat(sign + updatedBody);
    return num;
}

function gid(id) {
    const element = document.getElementById(id);
    if (!element) {
        throw new Error(`No element found for ID ${JSON.stringify(id)}`);
    }
    return element;
}

function onClick(id, handler) {
    gid(id).addEventListener("click", handler);
}

onClick("convert", () => {
    const str = gid("num").value;
    const num = convertToFloat(str);
    console.log(`${JSON.stringify(str)} => ${num}`);
});
<div>Enter a number using your locale's grouping and decimal separators, optionally prefaced with a minus sign (<code>-</code>) or plus sign (<code>+</code>):</div>
<input type="text" id="num" value="-123">
<input type="button" id="convert" value="Convert">

일반적으로 숫자 값에 대해 무료 텍스트 입력을 허용하지 않는 입력 필드를 사용하는 것을 고려해야 합니다.하지만 입력 형식을 추측해야 하는 경우가 있을 수 있습니다.예를 들어 독일에서 1.234,56은 미국에서 1,234.56을 의미합니다.쉼표를 10진수로 사용하는 국가 목록은 https://salesforce.stackexchange.com/a/21404 을 참조하십시오.

나는 다음 함수를 사용하여 가장 정확한 추측을 하고 모든 숫자가 아닌 문자를 제거합니다.

function parseNumber(strg) {
    var strg = strg || "";
    var decimal = '.';
    strg = strg.replace(/[^0-9$.,]/g, '');
    if(strg.indexOf(',') > strg.indexOf('.')) decimal = ',';
    if((strg.match(new RegExp("\\" + decimal,"g")) || []).length > 1) decimal="";
    if (decimal != "" && (strg.length - strg.indexOf(decimal) - 1 == 3) && strg.indexOf("0" + decimal)!==0) decimal = "";
    strg = strg.replace(new RegExp("[^0-9$" + decimal + "]","g"), "");
    strg = strg.replace(',', '.');
    return parseFloat(strg);
}   

여기서 해보세요: https://plnkr.co/edit/9p5Y6H?p=preview

예:

1.234,56 € => 1234.56
1,234.56USD => 1234.56
1,234,567€ => 1234567
1.234.567 => 1234567
1,234.567 => 1234.567
1.234 => 1234 // might be wrong - best guess
1,234 => 1234 // might be wrong - best guess
1.2345 => 1.2345
0,123 => 0.123

함수에는 다음과 같은 약점이 있습니다.로케일 형식에 따라 쉼표 또는 수천 구분 기호가 있을 수 있기 때문에 1,123 또는 1.123이 있을 경우 형식을 추측할 수 없습니다.이 특수한 경우 이 기능은 분리기를 수천 분리기로 취급하고 1123을 반환합니다.

그것들이 a to LocaleString을 포함했지만 파싱 방법은 포함하지 않았다는 것은 당황스럽습니다.적어도 인수가 없는 LocaleString은 IE6+에서 잘 지원됩니다.

i18n 솔루션을 위해 다음과 같이 생각했습니다.

먼저 사용자의 로케일 십진 구분자를 탐지합니다.

var decimalSeparator = 1.1;
decimalSeparator = decimalSeparator.toLocaleString().substring(1, 2);

그런 다음 문자열에 두 개 이상의 소수 구분 기호가 있을 경우 숫자를 정규화합니다.

var pattern = "([" + decimalSeparator + "])(?=.*\\1)";separator
var formatted = valor.replace(new RegExp(pattern, "g"), "");

마지막으로 숫자나 소수 구분 기호가 아닌 것은 모두 제거합니다.

formatted = formatted.replace(new RegExp("[^0-9" + decimalSeparator + "]", "g"), '');
return Number(formatted.replace(decimalSeparator, "."));
Number("2,299.00".split(',').join(''));   // 2299

분할 함수는 ""을 구분자로 사용하여 문자열을 배열로 분할하고 배열을 반환합니다.
조인 함수는 분할 함수에서 반환된 배열의 요소를 조인합니다.
Number() 함수는 결합된 문자열을 숫자로 변환합니다.

David Meister가 게시한 문제를 피하고 싶고 소수점 자리 수에 대해 확신이 있다면 모든 점과 쉼표를 바꾸고 100으로 나눌 수 있습니다.

var value = "2,299.00";
var amount = parseFloat(value.replace(/"|\,|\./g, ''))/100;

3개의 소수점이 있는 경우에는

var value = "2,299.001";
var amount = parseFloat(value.replace(/"|\,|\./g, ''))/1000;

parseInt, parseFloat Number를 사용하려면 사용자에게 달려 있습니다.또한 소수점 자리 수를 유지하려면 .to Fixed(...) 함수를 사용할 수 있습니다.

또는 다음과 같은 짧은 접근 방식을 시도해 보십시오.

const myNum =  +('2,299.00'.replace(",",""));

쉼표가 여러 개 있는 경우 Regex:

const myNum =  +('2,022,233,988.55'.replace(/,/g,""));
// -> myNum = 2022233988.55

(유사한 사용 사례에 대해) 배열된 사례는 다음과 같습니다.

이 배열의 합을 구하려면:

const numbers = ["11", "7", "15/25", "18/5", "12", "16/25"]

사용함으로써parseFloat소수점을 잃어서 정확한 합을 얻으려면 먼저 순방향 슬래시를 점으로 바꾼 다음 문자열을 실제 숫자로 변환해야 합니다.

그래서:

const currectNumbers = numbers.map(num => +(num.replace("/",".")))

// or the longer approach:
const currectNumbers = numbers
.map(num => num.replace("/","."))
.map(num => parseFloat(num));

그러면 축소 방법에 사용할 배열을 얻을 수 있습니다.

currectNumbers = [ 11, 7, 15.25, 18.5, 12, 16.25]

만약 당신이 수백만 단위의 숫자를 가지고 있다면 이 모든 대답은 실패합니다.

3,456,789는 단순히 3456을 교체 방법과 함께 반환하는 것입니다.

단순히 쉼표를 제거하는 가장 정확한 답은 다음과 같습니다.

var number = '3,456,789.12';
number.split(',').join('');
/* number now equips 3456789.12 */
parseFloat(number);

아니면 그냥.

number = parseFloat(number.split(',').join(''));

이것은 로케일에 있는 숫자를 일반 숫자로 변환합니다.소수점에도 적용됩니다.

function numberFromLocaleString(stringValue, locale){
    var parts = Number(1111.11).toLocaleString(locale).replace(/\d+/g,'').split('');
    if (stringValue === null)
        return null;
    if (parts.length==1) {
        parts.unshift('');
    }   
    return Number(String(stringValue).replace(new RegExp(parts[0].replace(/\s/g,' '),'g'), '').replace(parts[1],"."));
}
//Use default browser locale
numberFromLocaleString("1,223,333.567") //1223333.567

//Use specific locale
numberFromLocaleString("1 223 333,567", "ru") //1223333.567
const parseLocaleNumber = strNum => {
    const decSep = (1.1).toLocaleString().substring(1, 2);
    const formatted = strNum
        .replace(new RegExp(`([${decSep}])(?=.*\\1)`, 'g'), '')
        .replace(new RegExp(`[^0-9${decSep}]`, 'g'), '');
    return Number(formatted.replace(decSep, '.'));
};

이 기능을 사용하면 다음과 같은 여러 형식의 값을 포맷할 수 있습니다.1.234,56그리고.1,234.56, 그리고 심지어 같은 오류가 있더라도.1.234.56그리고.1,234,56

/**
 * @param {string} value: value to convert
 * @param {bool} coerce: force float return or NaN
 */
function parseFloatFromString(value, coerce) {
    value = String(value).trim();

    if ('' === value) {
        return value;
    }

    // check if the string can be converted to float as-is
    var parsed = parseFloat(value);
    if (String(parsed) === value) {
        return fixDecimals(parsed, 2);
    }

    // replace arabic numbers by latin
    value = value
    // arabic
    .replace(/[\u0660-\u0669]/g, function(d) {
        return d.charCodeAt(0) - 1632;
    })

    // persian
    .replace(/[\u06F0-\u06F9]/g, function(d) {
        return d.charCodeAt(0) - 1776;
    });

    // remove all non-digit characters
    var split = value.split(/[^\dE-]+/);

    if (1 === split.length) {
        // there's no decimal part
        return fixDecimals(parseFloat(value), 2);
    }

    for (var i = 0; i < split.length; i++) {
        if ('' === split[i]) {
            return coerce ? fixDecimals(parseFloat(0), 2) : NaN;
        }
    }

    // use the last part as decimal
    var decimal = split.pop();

    // reconstruct the number using dot as decimal separator
    return fixDecimals(parseFloat(split.join('') +  '.' + decimal), 2);
}

function fixDecimals(num, precision) {
    return (Math.floor(num * 100) / 100).toFixed(precision);
}
parseFloatFromString('1.234,56')
"1234.56"
parseFloatFromString('1,234.56')
"1234.56"
parseFloatFromString('1.234.56')
"1234.56"
parseFloatFromString('1,234,56')
"1234.56"

이곳의 많은 훌륭한 건축가들을 바탕으로, 저는 그것을 조금 단순화시켰습니다.

나는 그것을 사용하기 위해 사용하는 것을 선호합니다.best fit매커니즘

저와 같은 사용자가 덴마크 키보드를 가지고 있지만 맥이 영어인 것을 선호한다면 다음과 같은 도움이 됩니다.if (Number.isNaN(normalized)) return Number(value.replace(',', '.'));

만약 이것이 양식에 사용된다면, 나는 내가 사용해야 한다는 것을 발견했습니다.inputMode="numeric"보다는type="number".

function parseNumber(value, locales = undefined) {
  if (typeof value !== 'string') return value;
  const example = Intl.NumberFormat(locales).format('1.1');
  const normalized = Number(value.replace(example.charAt(1), '.'));
  if (Number.isNaN(normalized)) return Number(value.replace(',', '.'));
  return normalized;
}

/* test */

const tests = [
  {
    locale: 'en-US',
    candidate: 1.123,
    expected: 1.123,
  },
  {
    locale: 'en-US',
    candidate: '1.123',
    expected: 1.123,
  },
  {
    locale: 'fr-FR',
    candidate: '33.123',
    expected: 33.123,
  },
  {
    locale: 'fr-FR',
    candidate: '33,123',
    expected: 33.123,
  },
  {
    locale: 'da-DK',
    candidate: '45.123',
    expected: 45.123,
  },
  {
    locale: 'da-DK',
    candidate: '45,123',
    expected: 45.123,
  },
  {
    locale: 'en-US',
    candidate: '0.123',
    expected: 0.123,
  },
  {
    locale: undefined,
    candidate: '0,123',
    expected: 0.123,
  },
];

tests.forEach(({ locale, candidate, expected }) => {
  const parsed = parseNumber(candidate, locale);
  console.log(`${candidate} as ${typeof candidate} in ${locale}: ${parsed} === ${expected}? ${parsed === expected}`);
});

만약 당신이 all10n을 원한다면 이렇게 대답하세요.예를 들어 통화를 사용하지만 필요는 없습니다.오래된 브라우저를 지원해야 한다면 Intl 라이브러리를 폴리필(polyfilled)해야 합니다.

var value = "2,299.00";
var currencyId = "USD";
var nf = new Intl.NumberFormat(undefined, {style:'currency', currency: currencyId, minimumFractionDigits: 2});

value = nf.format(value.replace(/,/g, ""));

지원할 로케일이 조금만 있다면 간단한 규칙 몇 가지만 하드코딩하면 아마도 더 나아질 것입니다.

function parseNumber(str, locale) {
  let radix = ',';
  if (locale.match(/(en|th)([-_].+)?/)) {
    radix = '.';
  }
  return Number(str
    .replace(new RegExp('[^\\d\\' + radix + ']', 'g'), '')
    .replace(radix, '.'));
}

언급URL : https://stackoverflow.com/questions/11665884/how-can-i-parse-a-string-with-a-comma-thousand-separator-to-a-number

반응형