tengo una cadena como esta DOTA (UMA), MIAN (MIAN ISLAND), SOUTH TAKAK (PO) (JAB) lo esperado quiero así
Dota (UMA), Mian (MIAN ISLAND), South Takak (PO) (JAB)me intentaron así antes
// make title first _stringToTitle(str) { return str.replace( /\w\S*/g, function (txt) { return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); } ) } // get bracket _getBracket(str) { return str.match(/\(([^)]+)\)/g); } // now change to title and construct them all _changeWordToTitle(str) { const triLC = this._getBracket(str).join(" ") const toTitle = this._stringToTitle(str.replace(/\(.*\)/g, '')) return `${toTitle}${triLC}` }pero el resultado de mi código no parece tan preciso como este
Dota (UMA) (MIAN ISLAND) (PO) (JAB)¿Es eso posible usando solo una expresión regular para este caso?
Puedes usar
function _changeWordToTitle(str) { return str.replace(/(\([^()]*\))|(\S)(\S*)/g, (_, group1, group2, group3) => group1 || group2.toUpperCase() + group3.toLowerCase()); } console.log( _changeWordToTitle('Dota (UMA), Mian (MIAN ISLAND), South Takak (PO) (JAB)') )La expresión regular es
/(\([^()]*\))|(\S)(\S*)/gCoincide con cualquier subcadena entre paréntesis en el Grupo 1, o coincidirá con un espacio que no sea en blanco (capturado en el Grupo 2) y luego, en el Grupo 3, con cero o más espacios en blanco inmediatamente después.
Si el Grupo 1 coincide, devuelve la coincidencia tal como está (no se realiza ningún reemplazo); de lo contrario, el primer espacio que no sea en blanco (Grupo 2) se gira hacia arriba y el resto (Grupo 3) se gira hacia abajo.
Depende de cuántas suposiciones pueda hacer con la entrada.
const input = 'DOTA (UMA), MIAN (MIAN ISLAND), SOUTH TAKAK (PO) (JAB)';Por ejemplo, si está seguro de que no habrá más de dos palabras entre paréntesis, entonces podría tener un único enfoque de expresión regular como este:
// risky way (too many assumptions, fails with multi words in parens) input.replace( /([AZ])([AZ]*\s+)/g, ($0, $1, $2, i, source) => ( // ignore matches that starts with parens i > 0 && source[i -1] === '(' ? $0 : $1 + $2.toLowerCase() ) );Sin embargo, si los paréntesis entre paréntesis nunca están permitidos, seguramente un enfoque más sólido sería este:
// better way (not the fastest, arguably the easiest) input // capitalize them all .replace(/([AZ])([AZ]*)/g, (_, $1, $2) => $1 + $2.toLowerCase()) // uppercase in parens .replace(/\(([^)]+?)\)/g, (_, $1) => `(${$1.toUpperCase()})`) ;La única advertencia es que usa 2 RegExp, pero también hace mucho trabajo extra al poner mayúsculas y mayúsculas en una gran cantidad de texto.
Debido a la entrada, asumo que el texto entre paréntesis siempre será más largo que el que está antes del paréntesis, la solución anterior podría no ser súper rápida o súper eficiente.
Editar: este, sin embargo, es de esta respuesta , aquí por el bien de la documentación, y es un RegExp con paréntesis que se captura como noop:
input.replace( /(\([^)]*?\))|(\S)(\S*)/g, (_, $1, $2, $3) => ($1 || ($2 + $3.toLowerCase())) );Por último, pero no menos importante, algún analizador ad-hoc que use la cantidad mínima de almacenamiento dinámico y operaciones podría ser la mejor solución de todas.
// char-by-char way (how you'd do in C or without RegExp) function adHoc(input) { const output = []; const {length} = input; let i = 0; let prev = 0; let parens = false; while (i < length) { switch (true) { case input[i] === '(': case parens && input[i] === ')': parens = !parens; break; case !parens && 'A' <= input[i] && input[i] <= 'Z': const space = input.indexOf(' ', i); output.push( input.slice(prev, i + 1), input.slice(i + 1, space).toLowerCase() ); i = prev = space; break; } i++; } output.push(input.slice(prev)); return output.join(''); } adHoc(input); Esto también supone que siempre habrá paréntesis después de alguna palabra en mayúscula, por lo tanto, un espacio antes de dicho paréntesis, pero podría rescatar fácilmente si el space es negativo, en caso de que esa suposición no sea cierta.
El código anterior técnicamente podría ser portado a C, Rust o C++, por lo que puede tener su utilidad ágil si el rendimiento es crítico y la entrada es, potencialmente, enorme, pero estoy seguro de que JS haría un gran trabajo allí de todos modos.
Para evitar el uso de expresiones regulares complejas, puede crear un pequeño analizador para ello, algo como (consulte los comentarios dentro del fragmento para obtener más explicaciones):
console.log(parse(`DOTA (UMA), MIAN (MIAN ISLAND), SOUTH TAKAK (PO) (JAB)`)); function parse(str) { let result = ``; // [str] to lowercase, // first character of words to upper string // convert to Array const str2Parse = str .toLowerCase() .replace(/\b./gi, c => c.toUpperCase()) .split(``); // toUpper is a recursive function adding // uppercased characters to [result] while the // character is not a closing parenthesis const toUpper = (cc, rest) => { result += cc.toUpperCase(); const next = rest.shift(); if (next === `)`) { // done, add closing parenthesis and return result += next; return true; } // continue with the next character return toUpper(next, rest); }; // for every character of [str2Parse] while (str2Parse.length) { const c = str2Parse.shift(); // if c is an opening parenthesis, continue with toUpper // it will add uppercased characters to [result] if (c === `(`) { toUpper(c, str2Parse); continue; } result += c; } return result; }