Remplacer la dernière occurrence dans une chaîne de caractères

Beginner

This tutorial is from open-source community. Access the source code

Introduction

Dans ce laboratoire (lab), nous allons explorer la manipulation de chaînes de caractères en JavaScript en implémentant une fonction replaceLast. Contrairement à la méthode intégrée replace qui remplace la première occurrence d'un motif, notre fonction ciblera spécifiquement la dernière occurrence. Cette capacité est utile dans de nombreux scénarios du monde réel, comme la mise à jour des extensions de fichiers, la modification des URL ou le nettoyage de texte.

Tout au long de ce laboratoire, vous apprendrez les méthodes de manipulation de chaînes de caractères, les expressions régulières (regular expressions) et comment les combiner pour créer une fonction utilitaire pratique. À la fin de ce laboratoire, vous aurez une bonne compréhension des techniques de manipulation de chaînes de caractères en JavaScript et une fonction réutilisable pour vos projets de codage.

Comprendre le problème et configurer l'environnement

Avant de commencer à coder, comprenons ce que notre fonction replaceLast devrait faire :

  1. Accepter trois paramètres :

    • str : La chaîne de caractères d'entrée à modifier
    • pattern : La sous-chaîne ou l'expression régulière (regular expression) à rechercher
    • replacement : La chaîne de caractères à utiliser pour remplacer la dernière occurrence
  2. Retourner une nouvelle chaîne de caractères avec la dernière occurrence du motif remplacée.

Créons un fichier JavaScript pour implémenter notre fonction :

  1. Naviguez jusqu'au répertoire du projet dans l'explorateur de fichiers de WebIDE.
  2. Créez un nouveau fichier nommé replaceLast.js dans le répertoire replace-last.
  3. Ajoutez la structure de base suivante au fichier :
// Function to replace the last occurrence of a pattern in a string
function replaceLast(str, pattern, replacement) {
  // Our implementation will go here
  return str;
}

// We will add test cases here later

Pour vérifier que tout est configuré correctement, ajoutons un test simple :

// Example usage
console.log(replaceLast("Hello world world", "world", "JavaScript"));

Maintenant, exécutons notre code pour voir la sortie actuelle :

  1. Ouvrez le terminal dans WebIDE.
  2. Naviguez jusqu'au répertoire replace-last :
    cd ~/project/replace-last
    
  3. Exécutez le fichier JavaScript en utilisant Node.js :
    node replaceLast.js
    

Vous devriez voir Hello world world dans la sortie car notre fonction retourne actuellement simplement la chaîne de caractères originale sans apporter de modifications.

Implémentation de la logique principale de la fonction

Maintenant que nous comprenons le problème, implémentons la fonctionnalité principale de notre fonction replaceLast. Nous allons d'abord nous concentrer sur la gestion des motifs sous forme de chaînes de caractères, puis nous aborderons les expressions régulières (regular expressions) à l'étape suivante.

Lorsque le motif est une chaîne de caractères, nous pouvons utiliser la méthode lastIndexOf pour trouver la position de la dernière occurrence. Une fois que nous connaissons cette position, nous pouvons utiliser la méthode slice pour reconstruire la chaîne de caractères en insérant le remplacement.

Mettez à jour votre fonction replaceLast avec l'implémentation suivante :

function replaceLast(str, pattern, replacement) {
  // Ensure inputs are valid
  if (typeof str !== "string") {
    return str;
  }

  if (typeof pattern === "string") {
    // Find the position of the last occurrence
    const lastIndex = str.lastIndexOf(pattern);

    // If pattern not found, return original string
    if (lastIndex === -1) {
      return str;
    }

    // Rebuild the string with the replacement
    const before = str.slice(0, lastIndex);
    const after = str.slice(lastIndex + pattern.length);
    return before + replacement + after;
  }

  // We'll handle regex patterns in the next step
  return str;
}

Mettez à jour vos cas de test pour vérifier que la fonction gère correctement les motifs sous forme de chaînes de caractères :

// Test cases for string patterns
console.log(replaceLast("Hello world world", "world", "JavaScript")); // Should output: "Hello world JavaScript"
console.log(replaceLast("abcabcabc", "abc", "123")); // Should output: "abcabc123"
console.log(replaceLast("abcdef", "xyz", "123")); // Should output: "abcdef" (pattern not found)

Exécutez le code à nouveau pour voir la sortie mise à jour :

node replaceLast.js

Vous devriez maintenant voir la dernière occurrence du motif sous forme de chaîne de caractères remplacée dans chaque cas de test. Par exemple, "Hello world JavaScript" au lieu de "Hello world world".

Gestion des motifs d'expressions régulières

Maintenant, améliorons notre fonction pour gérer les motifs d'expressions régulières (regular expressions). Lorsque le motif est une expression régulière, nous devons :

  1. Trouver toutes les correspondances dans la chaîne de caractères
  2. Obtenir la dernière correspondance
  3. Remplacer cette dernière correspondance par la chaîne de remplacement

Mettez à jour votre fonction replaceLast pour gérer les motifs d'expressions régulières :

function replaceLast(str, pattern, replacement) {
  // Ensure inputs are valid
  if (typeof str !== "string") {
    return str;
  }

  // Handle string patterns
  if (typeof pattern === "string") {
    const lastIndex = str.lastIndexOf(pattern);
    if (lastIndex === -1) {
      return str;
    }
    const before = str.slice(0, lastIndex);
    const after = str.slice(lastIndex + pattern.length);
    return before + replacement + after;
  }

  // Handle regular expression patterns
  if (pattern instanceof RegExp) {
    // Create a new RegExp with global flag to find all matches
    const globalRegex = new RegExp(pattern.source, "g");

    // Find all matches
    const matches = str.match(globalRegex);

    // If no matches, return original string
    if (!matches || matches.length === 0) {
      return str;
    }

    // Get the last match
    const lastMatch = matches[matches.length - 1];

    // Find the position of the last match
    const lastIndex = str.lastIndexOf(lastMatch);

    // Rebuild the string with the replacement
    const before = str.slice(0, lastIndex);
    const after = str.slice(lastIndex + lastMatch.length);
    return before + replacement + after;
  }

  // If pattern is neither string nor RegExp, return original string
  return str;
}

Ajoutez des cas de test pour les motifs d'expressions régulières :

// Test cases for string patterns
console.log(replaceLast("Hello world world", "world", "JavaScript")); // Should output: "Hello world JavaScript"
console.log(replaceLast("abcabcabc", "abc", "123")); // Should output: "abcabc123"
console.log(replaceLast("abcdef", "xyz", "123")); // Should output: "abcdef" (pattern not found)

// Test cases for regular expression patterns
console.log(replaceLast("Hello world world", /world/, "JavaScript")); // Should output: "Hello world JavaScript"
console.log(replaceLast("123 456 789", /\d+/, "numbers")); // Should output: "123 456 numbers"
console.log(replaceLast("abcdef", /xyz/, "123")); // Should output: "abcdef" (pattern not found)

Exécutez le code à nouveau pour voir la sortie mise à jour :

node replaceLast.js

Les motifs sous forme de chaînes de caractères et les motifs d'expressions régulières devraient maintenant fonctionner correctement dans la fonction replaceLast.

Optimisation de la fonction et test des cas limites

Notre fonction fonctionne pour les cas de base, mais optimisons-la et gérons certains cas limites :

  1. Nous devrions vérifier si la chaîne de caractères d'entrée est vide.
  2. Nous pouvons simplifier la gestion des expressions régulières (regex).
  3. Nous devrions gérer les cas où le remplacement n'est pas une chaîne de caractères.

Mettez à jour votre fonction replaceLast avec ces optimisations :

function replaceLast(str, pattern, replacement) {
  // Ensure str is a string
  if (typeof str !== "string") {
    return str;
  }

  // If str is empty or pattern is not provided, return original string
  if (str === "" || pattern === undefined) {
    return str;
  }

  // Ensure replacement is a string
  if (replacement === undefined) {
    replacement = "";
  } else if (typeof replacement !== "string") {
    replacement = String(replacement);
  }

  // Handle string patterns
  if (typeof pattern === "string") {
    const lastIndex = str.lastIndexOf(pattern);
    if (lastIndex === -1) {
      return str;
    }
    return (
      str.slice(0, lastIndex) +
      replacement +
      str.slice(lastIndex + pattern.length)
    );
  }

  // Handle regular expression patterns
  if (pattern instanceof RegExp) {
    // Create a global version of the regex to find all matches
    const globalRegex = new RegExp(
      pattern.source,
      "g" + (pattern.ignoreCase ? "i" : "") + (pattern.multiline ? "m" : "")
    );

    // Find all matches
    const matches = str.match(globalRegex);

    // If no matches, return original string
    if (!matches || matches.length === 0) {
      return str;
    }

    // Get the last match
    const lastMatch = matches[matches.length - 1];

    // Find the position of the last match
    const lastIndex = str.lastIndexOf(lastMatch);

    // Rebuild the string with the replacement
    return (
      str.slice(0, lastIndex) +
      replacement +
      str.slice(lastIndex + lastMatch.length)
    );
  }

  // If pattern is neither string nor RegExp, return original string
  return str;
}

Ajoutons plus de cas de test pour couvrir les cas limites :

// Test cases for string patterns
console.log(replaceLast("Hello world world", "world", "JavaScript")); // Should output: "Hello world JavaScript"
console.log(replaceLast("abcabcabc", "abc", "123")); // Should output: "abcabc123"
console.log(replaceLast("abcdef", "xyz", "123")); // Should output: "abcdef" (pattern not found)

// Test cases for regular expression patterns
console.log(replaceLast("Hello world world", /world/, "JavaScript")); // Should output: "Hello world JavaScript"
console.log(replaceLast("123 456 789", /\d+/, "numbers")); // Should output: "123 456 numbers"
console.log(replaceLast("abcdef", /xyz/, "123")); // Should output: "abcdef" (pattern not found)

// Edge cases
console.log(replaceLast("", "abc", "123")); // Should output: "" (empty string)
console.log(replaceLast("abcdef", "", "123")); // Should output: "abcde123f" (empty pattern)
console.log(replaceLast("abcdef", "def", "")); // Should output: "abc" (empty replacement)
console.log(replaceLast("AbCdEf", /[a-z]/, "X")); // Should output: "AbCdEX" (case-sensitive regex)
console.log(replaceLast("AbCdEf", /[a-z]/i, "X")); // Should output: "AbCdEX" (case-insensitive regex)

Exécutez le code à nouveau pour voir la sortie mise à jour :

node replaceLast.js

Cette version de la fonction gère plus de cas limites et maintient un code propre. Vous disposez maintenant d'une fonction replaceLast robuste prête à être utilisée dans vos projets.

Création d'un module et utilisation de la fonction

Dans cette étape finale, nous allons convertir notre fonction en un véritable module JavaScript qui peut être importé et utilisé dans d'autres fichiers. C'est une pratique courante dans le développement JavaScript dans le monde réel.

Tout d'abord, créons un fichier de module pour notre fonction. Créez un nouveau fichier appelé replaceLastModule.js dans le répertoire replace-last :

/**
 * Replaces the last occurrence of a pattern in a string.
 *
 * @param {string} str - The input string.
 * @param {string|RegExp} pattern - The pattern to replace (string or RegExp).
 * @param {string} replacement - The replacement string.
 * @returns {string} - The string with the last occurrence replaced.
 */
function replaceLast(str, pattern, replacement) {
  // Ensure str is a string
  if (typeof str !== "string") {
    return str;
  }

  // If str is empty or pattern is not provided, return original string
  if (str === "" || pattern === undefined) {
    return str;
  }

  // Ensure replacement is a string
  if (replacement === undefined) {
    replacement = "";
  } else if (typeof replacement !== "string") {
    replacement = String(replacement);
  }

  // Handle string patterns
  if (typeof pattern === "string") {
    const lastIndex = str.lastIndexOf(pattern);
    if (lastIndex === -1) {
      return str;
    }
    return (
      str.slice(0, lastIndex) +
      replacement +
      str.slice(lastIndex + pattern.length)
    );
  }

  // Handle regular expression patterns
  if (pattern instanceof RegExp) {
    // Create a global version of the regex to find all matches
    const globalRegex = new RegExp(
      pattern.source,
      "g" + (pattern.ignoreCase ? "i" : "") + (pattern.multiline ? "m" : "")
    );

    // Find all matches
    const matches = str.match(globalRegex);

    // If no matches, return original string
    if (!matches || matches.length === 0) {
      return str;
    }

    // Get the last match
    const lastMatch = matches[matches.length - 1];

    // Find the position of the last match
    const lastIndex = str.lastIndexOf(lastMatch);

    // Rebuild the string with the replacement
    return (
      str.slice(0, lastIndex) +
      replacement +
      str.slice(lastIndex + lastMatch.length)
    );
  }

  // If pattern is neither string nor RegExp, return original string
  return str;
}

// Export the function
module.exports = replaceLast;

Maintenant, créons un autre fichier pour utiliser notre module. Créez un nouveau fichier appelé app.js dans le répertoire replace-last :

// Import the replaceLast function
const replaceLast = require("./replaceLastModule");

// Examples of using the replaceLast function
console.log(
  "Example 1:",
  replaceLast("Hello world world", "world", "JavaScript")
);
console.log("Example 2:", replaceLast("abcabcabc", "abc", "123"));
console.log("Example 3:", replaceLast("file.txt.backup.txt", ".txt", ".md"));
console.log("Example 4:", replaceLast("123 456 789", /\d+/, "numbers"));
console.log(
  "Example 5:",
  replaceLast("The fox jumped over the lazy dog", /[a-z]+/i, "cat")
);

// Practical examples
const filePath = "/path/to/my/file.txt";
console.log("File with new extension:", replaceLast(filePath, ".txt", ".md"));

const url = "https://example.com/products/category/item?color=red";
console.log("URL with updated parameter:", replaceLast(url, "red", "blue"));

const htmlTag = "<div class='container'><p>Text</p></div>";
console.log(
  "HTML with replaced tag:",
  replaceLast(htmlTag, /<\/?\w+>/g, "<span>")
);

Exécutez l'application pour voir comment le module fonctionne :

node app.js

Vous devriez voir la sortie avec tous les exemples montrant comment la fonction replaceLast peut être utilisée dans diverses situations.

Félicitations. Vous avez créé avec succès une fonction utilitaire JavaScript utile et l'avez emballée en tant que module qui peut être réutilisé dans vos projets.

Résumé

Dans ce laboratoire (lab), vous avez appris à créer une fonction utilitaire JavaScript qui remplace la dernière occurrence d'un motif dans une chaîne de caractères. Tout au long du processus, vous avez acquis des connaissances précieuses sur :

  • Les méthodes de manipulation de chaînes de caractères telles que lastIndexOf et slice
  • Le travail avec les expressions régulières (regular expressions) en JavaScript
  • La gestion de différents types d'entrées et de cas limites
  • La création et l'exportation de modules JavaScript
  • L'écriture de code propre et réutilisable avec une documentation appropriée

La fonction replaceLast que vous avez créée est une utilité pratique qui peut être utilisée dans de nombreux scénarios du monde réel, tels que la manipulation de chemins de fichiers, les modifications d'URL et le traitement de texte. Ce type de manipulation de chaînes de caractères est couramment nécessaire, mais n'est pas directement disponible dans la bibliothèque standard de JavaScript.

En comprenant les concepts et les techniques abordés dans ce laboratoire, vous êtes désormais mieux équipé pour résoudre des problèmes similaires de manipulation de chaînes de caractères dans vos futurs projets de programmation.