JavaScript эквивалентен printf/String.Format

Я ищу хороший JavaScript-эквивалент C/PHP printf() или для программистов на C#/Java, String.Format() (IFormatProvider для.NET).

Моим основным требованием является формат разделителя тысяч для чисел на данный момент, но что-то, что обрабатывает множество комбинаций (включая даты), было бы хорошо.

Я понимаю, что библиотека Ajax от Microsoft предоставляет версию String.Format(), но нам не нужны все издержки этой платформы.

61 ответ

Я не видел pyformat в списке, поэтому я решил добавить его:

console.log(pyformat( 'The {} {} jumped over the {}'
                , ['brown' ,'fox' ,'foobar']
                ))
console.log(pyformat('The {0} {1} jumped over the {1}'
                , ['brown' ,'fox' ,'foobar']
                ))
console.log(pyformat('The {color} {animal} jumped over the {thing}'
                , [] ,{color: 'brown' ,animal: 'fox' ,thing: 'foobaz'}
                ))

другое предложение - использовать строковый шаблон:

const getPathDadosCidades = (id: string) =>  `/clientes/${id}`

const getPathDadosCidades = (id: string, role: string) =>  `/clientes/${id}/roles/${role}`
/**
 * Format string by replacing placeholders with value from element with
 * corresponsing index in `replacementArray`.
 * Replaces are made simultaneously, so that replacement values like
 * '{1}' will not mess up the function.
 *
 * Example 1:
 * ('{2} {1} {0}', ['three', 'two' ,'one']) -> 'one two three'
 *
 * Example 2:
 * ('{0}{1}', ['{1}', '{0}']) -> '{1}{0}'
 */
function stringFormat(formatString, replacementArray) {
    return formatString.replace(
        /\{(\d+)\}/g, // Matches placeholders, e.g. '{1}'
        function formatStringReplacer(match, placeholderIndex) {
            // Convert String to Number
            placeholderIndex = Number(placeholderIndex);

            // Make sure that index is within replacement array bounds
            if (placeholderIndex < 0 ||
                placeholderIndex > replacementArray.length - 1
            ) {
                return placeholderIndex;
            }

            // Replace placeholder with value from replacement array
            return replacementArray[placeholderIndex];
        }
    );
}

Для использования с функциями успеха jQuery.ajax(). Передайте только один аргумент и строку замените свойствами этого объекта как {propertyName}:

String.prototype.format = function () {
    var formatted = this;
    for (var prop in arguments[0]) {
        var regexp = new RegExp('\\{' + prop + '\\}', 'gi');
        formatted = formatted.replace(regexp, arguments[0][prop]);
    }
    return formatted;
};

Пример:

var userInfo = ("Email: {Email} - Phone: {Phone}").format({ Email: "someone@somewhere.com", Phone: "123-123-1234" });

Я не видел String.format вариант:

String.format = function (string) {
    var args = Array.prototype.slice.call(arguments, 1, arguments.length);
    return string.replace(/{(\d+)}/g, function (match, number) {
        return typeof args[number] != "undefined" ? args[number] : match;
    });
};

С sprintf.js на месте - можно сделать отличный маленький формат-штука

String.prototype.format = function(){
    var _args = arguments 
    Array.prototype.unshift.apply(_args,[this])
    return sprintf.apply(undefined,_args)
}   
// this gives you:
"{%1$s}{%2$s}".format("1", "0")
// {1}{0}

Мне нужна была функция, которая могла бы отформатировать цену (указанную в центах) способом, предпочтительным для пользователя, и хитрая часть заключается в том, что формат указан пользователем - и я не ожидаю, что мои пользователи поймут синтаксис, похожий на printf или регулярные выражения и т. д. Мое решение несколько похоже на то, которое используется в Basic, поэтому пользователь просто помечает # местами для цифр, например:

simple_format(1234567,"$ ###,###,###.##")
"$ 12,345.67"
simple_format(1234567,"### ### ###,## pln")
"12 345,67 pln"

Я полагаю, что это довольно легко понять пользователю и довольно легко реализовать:

function simple_format(integer,format){
  var text = "";
  for(var i=format.length;i--;){
    if(format[i]=='#'){
      text = (integer%10) + text;
      integer=Math.floor(integer/10);
      if(integer==0){
        return format.substr(0,i).replace(/#(.*#)?/,"")+text;
      }
    }else{
      text = format[i] + text;
    }
  }
  return text;
}

В машинописном тексте создайте файл с именем format.ts и импортируйте все, что вам нужно для форматирования.

// contents of format.ts

interface String {
  format(...args: any[]): string;
}

if (!String.prototype.format) {
  String.prototype.format = function() {
    let a = this;
    let b: any;
    // tslint:disable-next-line: forin
    for (b in arguments) {
      a = a.replace(/%[a-z]/, arguments[b]);
    }
    return a;
  };
}

Для форматирования строки используйте этот код:

import './format';

console.log('Hello, %s!'.format('World'));

пример

String.prototype.format = function() {
  let a = this;
  let b;
  for (b in arguments) {
    a = a.replace(/%[a-z]/, arguments[b]);
  }
  return a;
};

console.log('Hello, %s!'.format('World'));

Если вам просто нужно отформатировать строку только с помощью спецификатора%s

function _sprintf(message){
    const regexp = RegExp('%s','g');
    let match;
    let index = 1;
    while((match = regexp.exec(message)) !== null) {
        let replacement = arguments[index];
        if (replacement) {
            let messageToArray = message.split('');
            messageToArray.splice(match.index, regexp.lastIndex - match.index, replacement);
            message = messageToArray.join('');
            index++;
        } else {
            break;
        }
    }

    return message;
}

_sprintf("my name is %s, my age is %s", "bob", 50); // my name is bob, my age is 50

arg функция:

/**
 * Qt stil arg()
 * var scr = "<div id='%1' class='%2'></div>".arg("mydiv").arg("mydivClass");
 */
String.prototype.arg = function() {
    var signIndex = this.indexOf("%");
    var result = this;
    if (signIndex > -1 && arguments.length > 0) {
        var argNumber = this.charAt(signIndex + 1);
        var _arg = "%"+argNumber;
        var argCount = this.split(_arg);
        for (var itemIndex = 0; itemIndex < argCount.length; itemIndex++) {
            result = result.replace(_arg, arguments[0]);
        }
    }
    return result;
}

String.prototype.format = function(){
    var final = String(this);
    for(let i=0; i<arguments.length;i++){
        final = final.replace(`%s${i+1}`, arguments[i])
    }
    return final || ''
}

console.log(("hello %s2 how %s3 you %s1").format('hi', 'hello', 'how'));
<h1 id="text">
   
</h1>

Вот очень короткая функция, которая выполняет подмножество printf и показывает результат в консоли разработчика:

      function L(...values)
    {
    // Replace each '@', starting with the text in the first arg
    console.log(values.reduce(function(str,arg) {return str.replace(/@/,arg)}));
    } // L

Вот тест:

      let a=[1,2,3];
L('a: [@]',a);

Вывод похож на:a=[1,2,3]

Прямо сейчас есть пакет locutus, который переводит функции других языков в Javascript, такие как php, python, ruby ​​и т. Д.

      const printf = require('locutus/php/strings/printf')
printf('Hello world');

Вы можете попробовать эту игровую площадку.

Если вам нужен printf, используйте printf

Похоже, 90% комментаторов никогда не использовали printf с более сложным форматом, чем просто%d. Интересно, как они выводят, например, денежные значения?

Итак, сначала мы настроим некоторые переменные для использования:

          const date = new Date();
    
    const locale = 'en-us';
    
    const wDay   = date.toLocaleString(locale, {weekday: 'short'});
    const month  = date.toLocaleString(locale, {month: 'long'});
    const year   = date.toLocaleString(locale, {year: 'numeric'});
    const minute = date.toLocaleString(locale, {minute: 'numeric'});
    const [hour, ap] = date.toLocaleString(locale, {hour: 'numeric', hour12:true}).split(' ');
    
    let mDay = date.toLocaleString(locale, {day: 'numeric'});
    
    switch(mDay % 10)
    {
        case 1:  mDay += 'st'; break;
        case 2:  mDay += 'nd'; break;
        case 3:  mDay += 'rd'; break;
        default: mDay += 'th'; break;
    }

Теперь, когда у нас есть все это, мы можем отформатировать строку следующим образом:

          const formatter = (...a) => `${a[0]}, the ${a[1]} of ${a[2]} ${a[3]} at ${a[4]}:${a[5]} ${a[6]}`;
    const formatted = formatter(wDay, mDay, month, year, hour, minute, ap);

Мы могли бы даже использовать именованные параметры для функции "форматирования":

          const formatter = (wDay, mDay, month, year, hour, minute, ap) => `${wDay}, the ${mDay} of ${month} ${year} at ${hour}:${minute} ${ap}`;
    const formatted = formatter(wDay, mDay, month, year, hour, minute, ap);

Если вы заметили, оба приведенных выше JS-шаблона являются результатом обратных вызовов. Если бы весь приведенный выше фрагмент кода был инкапсулирован в функцию, которая должна была возвращать отформатированную дату, было бы несложно представить, как таким же образом сконструировать произвольную функцию «форматировщика», которую можно было бы передать извне.

tl;dr вы можете повторно использовать шаблонные литералы, если вы поместите их в обратные вызовы и используете аргументы в качестве замены.

Я использую литеральный подход шаблона, как показано ниже:

      export const messages = {
  foo: (arg1, arg2) => `Hello ${arg1} ${arg2}`,
  bar: (arg1) => `Hello ${arg1}`,
}

Из файла:

      console.log(messages.foo('Bar', 'World'))
console.log(messages.bar('Foo'))

Существует также Globalize.format в проекте jQuery Globalize, официальный сервис глобализации для пользовательского интерфейса jQuery. Это хорошо, когда вам нужно культурно-ориентированное форматирование.

Этот работает с {0}, {1} и {}.

String.prototype.format = function format()
{                                                                                                               
  var msg = this;
  for(var i in arguments)
    msg = msg.replace(/\{\}/,arguments[i]).replace(new RegExp('\\{'+i+'\\}','g'),arguments[i]);
  return msg;
}

Мне нужно было решение на шаг вперед.

Шаблон, который я мог бы повторно использовать для генерации строк не только в объявлении, но и в случайное время во время выполнения.

Итак, я наткнулся на этот джиг:

       class Texplate{
    constructor(...args){
        this.data = args;
    }

    apply(...args){
        var text = "";
        var i = 0, j = 0, n = this.data.length, m = args.length;
        for(;i < n && j < m; i++, j++){
            text += this.data[i] + args[j];
        }

        for (; i < n; i++){
            text += this.data[i];
        }

        for (; j < m; j++){
            text += args[j];
        }

        return text;        
    }
}

Это позволяет создать текстовый шаблон, который работает внутри как алгоритм слияния массивов, начиная с текстового массива, определенного в конструкторе.

Пример использования:

      var Textplate example = new Texplate("Hello, ", "!"); 
console.log(example.apply("Frank"));
console.log(example.apply("Mary"));
console.log(example.apply());
console.log(example.apply("Frank", " Have a good day!"));

Я отвечаю на этот вопрос по следующим причинам.

  • sprintf / printf позволяет вам предоставлять аргументы переменной длины.
  • В самых популярных ответах используются литералы шаблонов . При использовании литералов шаблона вы должны знать количество имеющихся у вас аргументов.

Ответы @Braden Best и @David Spector кажутся обоснованными с моей точки зрения.

Я добавляю следующий ответ, чтобы кто-то мог найти ответ в одном месте.

Объяснение:

  • В этом методе вы передаете желаемую строку шаблона в первом параметре, имеющем заполнитель. Кроме того, вы можете передать столько заменителей, сколько захотите.
  • Затем он перебирает переданные значения и заменяет следующее текущим итерируемым значением.

В основном, если вы знаете, что делают Array.reduce и String.replace , вы понимаете код.

Вы можете изменить на все, что захотите. Кроме того, вам необходимо будет изменить :paramв этом случае внутри метода sprintf.

Я начал портировать JavaString.format (фактически новый Formatter().format()) для JavaScript. Начальная версия доступна по адресу:

https://github.com/RobAu/javascript.string.format

Вы можете просто добавить JavaScript и вызвать StringFormat.format("%.2f", [2.4]); и т.п.

Обратите внимание, что это еще не закончено, но отзывы приветствуются:)

Модифицированный код старого ответа /questions/8562252/javascript-ekvivalenten-printfstringformat/8562281#8562281 намного эффективнее (без медленного RegExp) и короче

      String.prototype.formatUnicorn = function () {
    let str = this.toString();
    if(!arguments.length) {
        return;
    };
    const [args] = arguments;
    for (const key of Object.keys(args)) {
        str = str.replaceAll(`{${key}}`, args[key]);
    };
    return str;
};

Применение:

      "{test} {test_2} {test}".formatUnicorn({"test": "hello", "test_2": "world"}); // yields hello world hello

эталон между новым и старым: https://jsben.ch/BRovx

bobjs может сделать это:

var sFormat = "My name is {0} and I am {1} years old."; 
var result = bob.string.formatString(sFormat, "Bob", 29); 
console.log(result); 
//output: 
//========== 
// My name is Bob and I am 29 years old. 
export function stringFormate (str: string, ...args: string[]) {
     return args.reduce((acc, curr, i) => acc.replace(new RegExp("\\{" + i + "\\}", 'g'), curr), str);
}
String.prototype.repeat = function(n) { 
    return new Array(++n).join(this); 
};

String.prototype.pad = function(requiredLength, paddingStr, paddingType) {    
    var n = requiredLength - this.length; 

    if (n) {
        paddingType = paddingType ? paddingType.toLowerCase() : '';
        paddingStr = paddingStr || ' ';
        paddingStr = paddingStr.repeat( Math.ceil(n / paddingStr.length) ).substr(0, n);

        if (paddingType == 'both') {
            n /= 2;
            return paddingStr.substr( 0, Math.ceil(n) ) + this + paddingStr.substr( 0, Math.floor(n) );
        }   

        if (paddingType == 'left') {
            return paddingStr + this;
        }

        return this + paddingStr;
    } 

    return this; 
}; 

// синтаксис аналогичен printf
// 'Привет, %s!'.format('мир') -> "Привет, мир!"
// '%.1s.%.1s. %s'.format('Иван', 'Иванович', 'Иванов') -> "И.И. Иванов"
String.prototype.format = function() {
    var i = 0, 
        params = arguments;

    return this.replace(/%(?:%|(?:(|[+-]+)(|0|'.+?)([1-9]\d*)?(?:\.([1-9]\d*))?)?(s|d|f))/g, function(match, sign, padding, width, precision, type) {
        if (match == '%%') { 
            return '%'; 
        }

        var v = params[i++];

        if (type == 'd') { 
            v = Math.round(v); 
        }
        else if (type == 'f') {
            v = v.toFixed(precision ? precision : 6);
        }

        if (/\+/.test(sign) && v > 0) {
            v = '+' + v;
        }

        v += '';

        if (type != 'f' && precision) {
            v = v.substr(0, precision);
        }

        if (width) {
            v = v.pad(width, padding == '' ? ' ' : padding[0] == "'" ? padding.substr(1) : padding, /-/.test(sign) ? 'right' : 'left'); 
        }

        return v;
    });
};

// this.name = 'Вася';
// console.log( 'Привет, ${name}!'.template(this) );
// "Привет, Вася!"
String.prototype.template = function(context) {
    return this.replace(/\$\{(.*?)\}/g, function(match, name) {
        return context[name];
    });
};

Вы можете использовать эту функцию

            String.prototype.format = function (args) {
            var str = this;
            return str.replace(String.prototype.format.regex, function(item) {
                var intVal = parseInt(item.substring(1, item.length - 1));
                var replace;
                if (intVal >= 0) {
                    replace = args[intVal];
                } else if (intVal === -1) {
                    replace = "{";
                } else if (intVal === -2) {
                    replace = "}";
                } else {
                    replace = "";
                }
                return replace;
            });
        };
        String.prototype.format.regex = new RegExp("{-?[0-9]+}", "g");

        // Sample usage.
        var str = "She {1} {0}{2} by the {0}{3}. {-1}^_^{-2}";
        str = str.format(["sea", "sells", "shells", "shore"]);
        alert(str);

Просто используйте шаблонные строки и стрелочные функции. Это даже делает их многоразовыми.

Это реализация /questions/8562252/javascript-ekvivalenten-printfstringformat/8562277#8562277 для CoffeeScript.

https://gist.github.com/eces/5669361

if String.prototype.format is undefined
  String.prototype.format = () ->
    _arguments = arguments
    this.replace /{(\d+)}/g, (match, number) ->
      if typeof _arguments[number] isnt 'undefined' then _arguments[number] else match

Не самая рекомендуемая функция в мире, но работает.

Если вам нужен sprintf, просто скопируйте и вставьте эту же функцию и измените return console.log(sb) чтобы просто return sb.

printf = function(s, /*args...*/) {
    a = arguments;
    al = a.length;
    
    if (al <= 1) return -2;
    if (al >= 2 && s.toLowerCase().search(/%[a-z]/) == -1) return -1;

    sb = s;
    for (i = 1; i <= al - 1; i++) {
        sb = sb.replace(/%[a-z]/, a[i]);
    }

    return console.log(sb);
}

var someString = "Hello %s\nIt's %s:%s %s now.\nThe day is %s\n";
printf(someString, "StackruUser", "5", "48", "PM", "beautiful");

Аналог функции sprintf() в JavaScript как фильтр Vue и расширение String.prototype.format():

       /**
 * Returns a formatted string.
 *
 * @param template
 * @param values
 * @return string
 */
String.format = function (template, ...values) {
    let i = -1;

    function callback(exp, p0, p1, p2, p3, p4) {
        if (exp === '%%') return '%';
        if (values[++i] === undefined) return undefined;

        exp = p2 ? parseInt(p2.substr(1)) : undefined;

        let base = p3 ? parseInt(p3.substr(1)) : undefined;
        let val;

        switch (p4) {
            case 's': val = values[i]; break;
            case 'c': val = values[i][0]; break;
            case 'f': val = parseFloat(values[i]).toFixed(exp); break;
            case 'p': val = parseFloat(values[i]).toPrecision(exp); break;
            case 'e': val = parseFloat(values[i]).toExponential(exp); break;
            case 'x': val = parseInt(values[i]).toString(base ? base : 16); break;
            case 'd': val = parseFloat(parseInt(values[i], base ? base : 10).toPrecision(exp)).toFixed(0); break;
        }
        val = typeof (val) == 'object' ? JSON.stringify(val) : val.toString(base);
        let sz = parseInt(p1); /* padding size */
        let ch = p1 && p1[0] === '0' ? '0' : ' '; /* isnull? */

        while (val.length < sz) val = p0 !== undefined ? val + ch : ch + val; /* isminus? */

        return val;
    }

    let regex = /%(-)?(0?[0-9]+)?([.][0-9]+)?([#][0-9]+)?([scfpexd%])/g;

    return template.replace(regex, callback);
}

String.prototype.format = function() {
    return String.format(this, ...arguments);
}

const StringFormat = {
    install: (Vue, options) => {
        Vue.filter('format', function () {
            return String.format(...arguments);
        });
    },
};

export default StringFormat;

Исходный ответ: эквивалент JavaScript printf/String.Format

Другие вопросы по тегам