JavaRush /وبلاگ جاوا /Random-FA /جایگزین رشته در جاوا

جایگزین رشته در جاوا

در گروه منتشر شد
در کار یک برنامه نویس، اغلب برخی از کارها یا اجزای آنها ممکن است تکرار شوند. بنابراین، امروز می خواهم به موضوعی بپردازم که اغلب در کار روزانه هر توسعه دهنده جاوا با آن مواجه می شود. جایگزین رشته در جاوا - 1فرض کنید رشته خاصی را از یک متد دریافت می کنید. و همه چیز در مورد آن خوب به نظر می رسد، اما چیز کوچکی وجود دارد که برای شما مناسب نیست. به عنوان مثال، جداکننده مناسب نیست و شما به یکی دیگر (یا اصلاً) نیاز دارید. در چنین شرایطی چه می توان کرد؟ طبیعتاً از متدهای replaceکلاس استفاده کنید String.

جایگزینی رشته جاوا

شی نوع Stringدارای چهار تغییر از روش جایگزینی است replace:
  • replace(char, char);
  • replace(CharSequence, CharSequence);
  • replaceFirst(String, String);
  • replaceAll(String, String).
هدف همه این روش ها یکسان است - جایگزینی بخشی از یک رشته با رشته دیگری. بیایید نگاهی دقیق تر به آنها بیندازیم. 1.replace(char, char) String replace(char oldChar, char newChar) - همه رخدادهای شخصیت آرگومان اول را oldCharبا آرگومان دوم جایگزین می کند newChar. در این مثال، کاما را با نقطه ویرگول جایگزین می کنیم:
String value = "In JavaRush, Diego the best, Diego is Java God".replace(',', ';');
System.out.println(value);
خروجی کنسول:
In JavaRush; Diego the best; Diego is Java God
2.replace(CharSequence, CharSequence) هر زیر رشته از یک رشته را که با یک دنباله مشخص از کاراکترها مطابقت دارد، با دنباله ای از کاراکترهای جایگزین جایگزین می کند.
String value = "In JavaRush, Diego the best, Diego is Java God".replace("Java", "Rush");
System.out.println(value);
نتیجه:
In RushRush, Diego the best, Diego is Rush God
3.replaceFirst(String, String) String replaceFirst(String regex, String replacement) - اولین رشته فرعی را که با عبارت منظم مشخص شده مطابقت دارد با یک رشته جایگزین جایگزین می کند. وقتی از یک عبارت منظم نامعتبر استفاده می کنید، می توانید PatternSyntaxException را بگیرید (که چیز خوبی نیست). در این مثال، بیایید نام ربات قهرمان را جایگزین کنیم:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceFirst("Diego", "Amigo");
System.out.println(value);
خروجی کنسول:
In JavaRush, Amigo the best, Diego is Java God
همانطور که می بینیم، فقط اولین ورودی "دیگو" تغییر کرده است، اما موارد بعدی کنار گذاشته شده اند - یعنی دست نخورده. 4. replaceAll()در جاوا String replaceAll(String regex, String replacement) - این روش همه رخدادهای یک رشته فرعی در یک رشته regexرا با replacement. یک عبارت منظم می تواند به عنوان اولین آرگومان استفاده شود regex. به عنوان مثال، بیایید سعی کنیم جایگزین قبلی را با نام ها انجام دهیم، اما با یک روش جدید:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceAll("Diego", "Amigo");
System.out.println(value);
خروجی کنسول:
In JavaRush, Amigo the best, Amigo is Java God
همانطور که می بینیم، تمام نمادها به طور کامل با نمادهای ضروری جایگزین شده اند. من فکر می کنم آمیگو راضی خواهد شد =)

عبارات با قاعده

در بالا گفته شد که می توان با استفاده از یک عبارت منظم جایگزین کرد. ابتدا بیایید خودمان روشن کنیم که عبارت منظم چیست؟ عبارات منظم یک زبان رسمی برای جستجو و دستکاری زیر رشته ها در متن، بر اساس استفاده از متاکاراکترها (کاراکترهای عام) هستند. به عبارت ساده، الگویی از کاراکترها و فراکاراکترها است که یک قانون جستجو را تعریف می کند. به عنوان مثال: \D- الگویی که هر کاراکتر غیر دیجیتالی را توصیف می کند. \d- هر کاراکتر عددی را تعریف می کند که می تواند به صورت [0-9]; [a-zA-Z]- الگویی که نویسه‌های لاتین از a تا z را توصیف می‌کند، به حروف بزرگ و کوچک حساس نیست. برنامه را در متد replaceAllکلاس در نظر بگیرید String:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceAll("\\s[a-zA-Z]{5}\\s", " Amigo ");
System.out.println(value);
خروجی کنسول:
In JavaRush, Amigo the best, Amigo is Java God
\\s[a-zA-Z]{5}\\s- کلمه ای از 5 کاراکتر لاتین را توصیف می کند که با فاصله احاطه شده است. بر این اساس، این الگو با رشته ای که از آن عبور کردیم جایگزین می شود.

جاوا regex جایگزین

اساسا برای استفاده از عبارات منظم در جاوا، قابلیت های java.util.regex. کلاس های کلیدی عبارتند از:
  1. Pattern- کلاسی که یک نسخه کامپایل شده از یک عبارت منظم را ارائه می دهد.
  2. Matcher- این کلاس الگو را تفسیر می کند و مطابقت ها را در رشته ای که دریافت می کند تعیین می کند.
به طور معمول، این دو کلاس با هم کار می کنند. بنابراین، شی قبلی ما چگونه خواهد بود، اما با کمک Matcherو Pattern:
Pattern pattern = Pattern.compile("\\s[a-zA-Z]{5}\\s");
Matcher matcher = pattern.matcher("In JavaRush, Diego the best, Diego is Java God");
String value = matcher.replaceAll(" Amigo ");
System.out.println(value);
و نتیجه گیری ما یکسان خواهد بود:
In JavaRush, Amigo the best, Amigo is Java God
در این مقاله می توانید در مورد عبارات منظم بیشتر بخوانید .

جایگزین جایگزینی همه

شکی نیست که روش ها بسیار چشمگیر replaceهستند String، اما نمی توان این واقعیت را نادیده گرفت که یک شی Stringاست immutable، یعنی بعد از ایجاد آن قابل تغییر نیست. بنابراین، هنگامی که برخی از قسمت‌های یک رشته را با استفاده از متدها جایگزین می‌کنیم replace، شی را تغییر نمی‌دهیم String، بلکه هر بار یک شی جدید با محتویات لازم ایجاد می‌کنیم. اما ایجاد یک شی جدید هر بار زمان زیادی می برد، اینطور نیست؟ مخصوصاً وقتی سؤال چند شیء نیست، بلکه چند صد یا حتی هزاران است. خواه ناخواه، شما شروع به فکر کردن در مورد جایگزین می کنید. و چه جایگزین هایی داریم؟ جایگزین رشته در جاوا - 2هوم... وقتی صحبت از Stringویژگی آن می شود immutable، بلافاصله به گزینه های جایگزین فکر می کنید، اما نه immutable، یعنی StringBuilder/StringBuffer . همانطور که به یاد داریم، این کلاس ها در واقع تفاوتی ندارند، به جز اینکه StringBufferبرای استفاده در یک محیط چند رشته ای بهینه شده اند، بنابراین StringBuilderدر استفاده تک رشته ای تا حدودی سریعتر کار می کنند. بر این اساس، امروز ما StringBuilder. از این کلاس استفاده خواهیم کرد که روش های جالب زیادی دارد، اما به طور خاص در حال حاضر ما به replace. StringBuilder replace(int start, int end, String str)- این روش کاراکترهای یک رشته فرعی از این دنباله را با کاراکترهای رشته مشخص شده جایگزین می کند. رشته فرعی از ابتدای مشخص شده شروع می شود و تا کاراکتر انتهای فهرست -1یا تا پایان دنباله در صورت وجود چنین کاراکتری ادامه می یابد. بیایید به یک مثال نگاه کنیم:
StringBuilder strBuilder = new StringBuilder("Java Rush");
strBuilder.replace(5, 9, "God");
System.out.println(strBuilder);
نتیجه:
Java God
همانطور که می بینید، بازه ای را که می خواهیم رشته را در آن بنویسیم، نشان می دهیم و یک رشته فرعی در بالای آنچه در فاصله است می نویسیم. بنابراین، با استفاده از کمک، StringBuilderیک آنالوگ از روش را دوباره ایجاد می کنیم replaceall java. چگونه به نظر خواهد رسید:
public static String customReplaceAll(String str, String oldStr, String newStr) {

   if ("".equals(str) || "".equals(oldStr) || oldStr.equals(newStr)) {
       return str;
   }
   if (newStr == null) {
       newStr = "";
   }
   final int strLength = str.length();
   final int oldStrLength = oldStr.length();
   StringBuilder builder = new StringBuilder(str);

   for (int i = 0; i < strLength; i++) {
       int index = builder.indexOf(oldStr, i);

       if (index == -1) {
           if (i == 0) {
               return str;
           }
           return builder.toString();
       }
       builder = builder.replace(index, index + oldStrLength, newStr);

   }
       return builder.toString();
}
در نگاه اول ترسناک است، اما با کمی درک می توانید درک کنید که همه چیز چندان پیچیده و کاملا منطقی نیست. ما سه استدلال داریم:
  • str- خطی که در آن می خواهیم چند رشته فرعی را جایگزین کنیم.
  • oldStr- نمایش زیر رشته هایی که جایگزین خواهیم کرد.
  • newStr- با چه چیزی جایگزین خواهیم کرد.
ifما به اولین مورد نیاز داریم تا داده های دریافتی را بررسی کند، و اگر رشته خالی strباشد oldStrیا زیررشته جدید newStrبرابر با رشته قبلی باشد oldStr، اجرای متد بی معنی خواهد بود. بنابراین، رشته اصلی - str. در مرحله بعد، ما newStrرا بررسی می کنیم null، و اگر این مورد باشد، آن را به یک قالب رشته خالی تبدیل می کنیم که برای ما راحت تر است - "". سپس اعلان متغیرهای مورد نیاز خود را داریم:
  • طول کل رشته str;
  • طول زیر رشته oldStr;
  • شی StringBuilderاز یک رشته مشترک
ما یک حلقه را شروع می کنیم که باید چندین بار برابر طول رشته کل اجرا شود (اما، به احتمال زیاد، این هرگز اتفاق نخواهد افتاد). با استفاده از متد کلاس StringBuilder- indexOf- شاخص اولین رخداد رشته فرعی مورد نظر خود را می یابیم. متأسفانه، می‌خواهم توجه داشته باشم که indexOfبا عبارات منظم کار نمی‌کند، بنابراین روش نهایی ما فقط با وقوع رشته‌ها کار می‌کند (( اگر این شاخص برابر باشد -1، پس دیگر از این رخدادها در شی فعلی وجود نخواهد داشت StringBuilder. بنابراین با نتیجه علاقه از روش خارج می‌شویم: در ما موجود است StringBuilderکه Stringبا استفاده از آن به آن تبدیل می‌کنیم toString. اگر شاخص ما -1در اولین تکرار حلقه برابر باشد، رشته فرعی که باید جایگزین شود در حالت کلی نبوده است. بنابراین در چنین شرایطی ما به سادگی رشته کلی را برمی گردانیم و سپس از روشی که در بالا توضیح داده شد replaceبرای StringBuilderاستفاده از اندیس پیدا شده رخداد برای نشان دادن مختصات رشته فرعی که باید جایگزین شود استفاده می شود.این حلقه اجرا خواهد شد. اگر رشته فقط از کاراکتری تشکیل شده باشد که باید جایگزین شود، در این صورت است که حلقه به طور کامل اجرا می شود و نتیجه را StringBuilderبه رشته تبدیل می کنیم. باید صحت این روش را بررسی کنیم، درست است؟ بیایید تستی بنویسیم که عملکرد روش را در موقعیت های مختلف بررسی می کند:
@Test
public void customReplaceAllTest() {
   String str = "qwertyuiop__qwertyuiop__";

   String firstCase = Solution.customReplaceAll(str, "q", "a");
   String firstResult = "awertyuiop__awertyuiop__";
   assertEquals(firstCase, firstResult);

   String secondCase = Solution.customReplaceAll(str, "q", "ab");
   String secondResult = "abwertyuiop__abwertyuiop__";
   assertEquals(secondCase, secondResult);

   String thirdCase = Solution.customReplaceAll(str, "rtyu", "*");
   String thirdResult = "qwe*iop__qwe*iop__";
   assertEquals(thirdCase, thirdResult);

   String fourthCase = Solution.customReplaceAll(str, "q", "");
   String fourthResult = "wertyuiop__wertyuiop__";
   assertEquals(fourthCase, fourthResult);

   String fifthCase = Solution.customReplaceAll(str, "uio", "");
   String fifthResult = "qwertyp__qwertyp__";
   assertEquals(fifthCase, fifthResult);

   String sixthCase = Solution.customReplaceAll(str, "", "***");
   assertEquals(sixthCase, str);

   String seventhCase = Solution.customReplaceAll("", "q", "***");
   assertEquals(seventhCase, "");
}
را می توان به 7 تست مجزا تقسیم کرد که هر کدام مسئول مورد آزمایشی خود خواهند بود. پس از راه اندازی آن، خواهیم دید که سبز است، یعنی موفق است. خب، به نظر می رسد که تمام است. اگرچه صبر کنید، اما در بالا گفتیم که این روش بسیار سریعتر replaceAllاز String. خوب بگذار ببینیم:
String str = "qwertyuiop__qwertyuiop__";
long firstStartTime = System.nanoTime();

for (long i = 0; i < 10000000L; i++) {
   str.replaceAll("tyu", "#");
}

double firstPerformance = System.nanoTime() - firstStartTime;

long secondStartTime = System.nanoTime();

for (long i = 0; i < 10000000L; i++) {
   customReplaceAll(str, "tyu", "#");
}

double secondPerformance = System.nanoTime() - secondStartTime;

System.out.println("Performance ratio  - " +  firstPerformance / secondPerformance);
در مرحله بعد، این کد سه بار اجرا شد و به نتایج زیر رسیدیم: خروجی کنسول:
Performance ratio  - 5.012148941181627
 
Performance ratio  - 5.320637176017641
 
Performance ratio  - 4.719192686500394
همانطور که می بینیم، به طور متوسط ​​روش ما 5 برابر بیشتر از replaceAllکلاس کلاسیک کارآمدتر است! Stringخوب، در نهایت، بیایید همان چک را اجرا کنیم، اما، به اصطلاح، بیهوده. به عبارت دیگر در موردی که مطابقت پیدا نشود. بیایید رشته جستجو را از "tyu"به جایگزین کنیم "--". سه اجرا نتایج زیر را به همراه داشت: خروجی کنسول:
Performance ratio  - 8.789647093542246
 
Performance ratio  - 9.177105482660881
 
Performance ratio  - 8.520964375227406
به طور متوسط، عملکرد برای مواردی که هیچ مسابقه ای پیدا نشد، 8.8 برابر افزایش یافت! جایگزین رشته در جاوا - 4
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION