Java字符串问题:替换字符串的特定部分

huangapple go评论78阅读模式
英文:

Java string problem: replacing specific part of a string

问题

这是你提供的代码的翻译:

一个名为*replacement*的方法将给定字符串a中带有[Name]{Name}括号的所有名称替换为电话号码如果是[]括号或电子邮件如果是{}括号)。地址簿用数组tel表示其元素可以是"Tel Name telephoneNumber""Mail Name mail"格式例如如果输入是"You can contact jake via phone number [Jake] or via email {Jake}"输出应为"You can contact jake via phone number +12345 or via email jake@gmail.com"*tel*元素为"Tel Jake +12345""Mail Jake jake@gmail.com"如果给定的名称在地址簿中不存在则不对字符串进行任何操作我遇到的问题是在替换子字符串时我使用的是replaceFirst方法它将替换我想要替换的子字符串的第一个出现位置

也许更简洁的问题是如何替换字符串的特定部分

```java
public static String replacement(String a, String[] tel) {
    for (int i = 0; i < a.length() - 1; i++) {
        char c = a.charAt(i);
        if (c == '[') {
            int ind = a.indexOf(']', i);
            String name = a.substring(i + 1, ind);
            for (int j = 0; j < tel.length; j++) {
                int ind1 = tel[j].indexOf(' ', 4);
                String name1 = tel[j].substring(4, ind1);
                String p = tel[j].substring(0, 3);
                String help = "Tel";
                int temp = p.compareTo(help);
                if (name.equals(name1) && temp == 0) {
                    String telephone = tel[j].substring(ind1 + 1, tel[j].length());
                    a = a.replaceFirst(name, telephone);
                }
            }
        }
        if (c == '{') {
            int ind = a.indexOf('}', i);
            String name = a.substring(i + 1, ind);
            for (int j = 0; j < tel.length; j++) {
                int ind1 = tel[j].indexOf(' ', 5);
                String name1 = tel[j].substring(5, ind1);
                String p = tel[j].substring(0, 4);
                if (name.equals(name1) && p.compareTo("Mail") == 0) {
                    String mail = tel[j].substring(ind1 + 1, tel[j].length());
                    a = a.replaceFirst(name, mail);
                }
            }
        }
    }
    return a;
}

主方法:

String a = "In NY you can contact peter via telephone number [Peter] or e-mail {Peter}. In London you can contact anna via telephone number [Anna] or e-mail {Anna}."
+ "In Chicago you can contact shawn via telephone number [Shawn] or e-mail {Shawn}";
String[] tel = {"Mail Peter peter@gmail.com", "Tel Anna +3456", "Tel Shawn +1234", "Mail Shawn shawn@yahoo.com"};
String t = replacement(a, tel);
System.out.println(t);

控制台输出:

In NY you can contact peter via telephone number [peter@gmail.com] or e-mail {peter@gmail.com}.
In London you can contact anna via telephone number [+3456] or e-mail {Anna}.In Chicago you can 
contact shawn via telephone number [+1234] or e-mail {shawn@yahoo.com}
英文:

A method replacement replaces all names (from given String a) in [Name] or {Name} brackets, with telephone numbers if [] these brackets, or e-mails if {} these brackets. The address book is represented with array tel, whose elements can be "Tel Name telephoneNumber" or "Mail Name mail". For example if input is: "You can contact jake via phone number [Jake] or via email {Jake}", output should be "You can contact jake via phone number +12345 or via email jake@gmail.com", and tel elements are "Tel Jake +12345" and "Mail Jake jake@gmail.com". If the given name does not exist in address book do nothing with the string. The problem that I have is when it comes to replacing substrings I use method replaceFirst which will replace the first occurrence of the substring that I want to replace.

Maybe the shorter question would be how to replace specific part of string?

public static String replacement(String a, String[] tel) {
for (int i = 0; i&lt;a.length()-1; i++) {
char c = a.charAt(i);
if (c==&#39;[&#39;) {
int ind = a.indexOf(&#39;]&#39;, i);
String name = a.substring(i+1, ind);
for (int j=0; j&lt;tel.length; j++) {
int ind1 = tel[j].indexOf(&#39; &#39;, 4);
String name1 = tel[j].substring(4, ind1);
String p = tel[j].substring(0,3);
String help = &quot;Tel&quot;;
int temp = p.compareTo(help);
if (ime.equals(ime1)==true &amp;&amp; temp==0) {
String telephone = tel[j].substring(ind1+1, tel[j].length());
a = a.replaceFirst(name, telephone);
} 
} 
}
if (c==&#39;{&#39;) {
int ind = a.indexOf(&#39;}&#39;, i);
String name = a.substring(i+1, ind);
for (int j=0; j&lt;tel.length; j++) {
int ind1 = tel[j].indexOf(&#39; &#39;, 5);
String name1 = tel[j].substring(5, ind1);
String p = tel[j].substring(0,4); 
if (name.equals(name1) &amp;&amp; p.compareTo(&quot;Mail&quot;)==0) {
String mail = tel[j].substring(ind1+1, tel[j].length());
a = a.replaceFirst(name, mail);
}
}
}
}
return a;
}

Main:

String a = &quot;In NY you can contact peter via telephone number [Peter] or e-mail {Peter}. In London you can contact anna via telephone number [Anna] or e-mail {Anna}.&quot;
+ &quot;In Chicago you can contact shawn via telephone number [Shawn] or e-mail {Shawn}&quot;;
String [] tel = {&quot;Mail Peter peter@gmail.com&quot;, &quot;Tel Anna +3456&quot;,&quot;Tel Shawn +1234&quot;, &quot;Mail Shawn shawn@yahoo.com&quot;};
String t = replacement(a,tel);
System.out.println(t);

Console:

In NY you can contact peter via telephone number [peter@gmail.com] or e-mail {peter@gmail.com}.
In London you can contact anna via telephone number [+3456] or e-mail {Anna}.In Chicago you can 
contact shawn via telephone number [+1234] or e-mail {shawn@yahoo.com}

答案1

得分: 2

不要有别的内容,以下是翻译好的部分:

Instead of encoding the type of the data (email vs phone number) and the replacement key into strings, I would put the data into separate variables and use data structures like `Map`:

将数据的类型电子邮件 vs 电话号码和替换键编码为字符串的方式我会将数据放入单独的变量中并使用诸如 `Map` 之类的数据结构

Map&lt;String, String&gt; tel = Map.of(&quot;Anna&quot;, &quot;+3456&quot;, &quot;Shawn&quot;, &quot;+1234&quot;);
Map&lt;String, String&gt; mail = Map.of(&quot;Peter&quot;, &quot;peter@gmail.com&quot;, &quot;Shawn&quot;, &quot;shawn@yahoo.com&quot;);
String t = replacement(a, tel, mail);

The `replacement` function could use a *regular expression* to find the substrings that match the key words you want to replace `[something]` and `{something}`. It would check which one it found, and add a replacement using the telephone or email it finds in the map data structure.

`replacement` 函数可以使用*正则表达式*来查找与您想要替换的关键词 `[something]``{something}` 匹配的子字符串它将检查找到了哪一个并使用在映射数据结构中找到的电话号码或电子邮件添加替换

private static String replacement(String a, Map&lt;String, String&gt; tel, Map&lt;String, String&gt; mail) {
    Pattern compile = Pattern.compile(&quot;\\{(.*?)\\}|\\[(.*?)\\]&quot;);
    Matcher matcher = compile.matcher(a);
    StringBuilder sb = new StringBuilder();

    // Find substrings matching {something} and [something]
    while (matcher.find()) {
        String matched = matcher.group(0);

        // Which was it, { or [ ? 检查是 { 还是 [ ?
        if (matched.charAt(0) == &#39;{&#39;) {
            // Email. Replace from &quot;mail&quot; 电子邮件。从“mail”替换
            String emailAddress = mail.getOrDefault(matcher.group(1), matched);
            matcher.appendReplacement(sb, emailAddress);
        } else if (matched.charAt(0) == &#39;[&#39;) {
            // Telephone. Replace from &quot;tel&quot; 电话号码。从“tel”替换
            String phoneNumber = tel.getOrDefault(matcher.group(2), matched);
            matcher.appendReplacement(sb, phoneNumber);
        }
    }
    matcher.appendTail(sb);
    return sb.toString();
}
英文:

Instead of encoding the type of the data (email vs phone number) and the replacement key into strings, I would put the data into separate variables and ues data structures like Map:

Map&lt;String, String&gt; tel = Map.of(&quot;Anna&quot;, &quot;+3456&quot;, &quot;Shawn&quot;, &quot;+1234&quot;);
Map&lt;String, String&gt; mail = Map.of(&quot;Peter&quot;, &quot;peter@gmail.com&quot;, &quot;Shawn&quot;, &quot;shawn@yahoo.com&quot;);
String t = replacement(a, tel, mail);

The replacement function could use a regular expression to find the substrings that match the key words you want to replace [something] and {something}. It would check which one it found, and add a replacement using the telephone or email it finds in the map data structure.

private static String replacement(String a, Map&lt;String, String&gt; tel, Map&lt;String, String&gt; mail) {
Pattern compile = Pattern.compile(&quot;\\{(.*?)\\}|\\[(.*?)\\]&quot;);
Matcher matcher = compile.matcher(a);
StringBuilder sb = new StringBuilder();
// Find substrings matching {something} and [something]
while (matcher.find()) {
String matched = matcher.group(0);
// Which was it, { or [ ?
if (matched.charAt(0) == &#39;{&#39;) {
// Email. Replace from &quot;mail&quot;
String emailAddress = mail.getOrDefault(matcher.group(1), matched);
matcher.appendReplacement(sb, emailAddress);
} else if (matched.charAt(0) == &#39;[&#39;) {
// Telephone. Replace from &quot;tel&quot;
String phoneNumber = tel.getOrDefault(matcher.group(2), matched);
matcher.appendReplacement(sb, phoneNumber);
}
}
matcher.appendTail(sb);
return sb.toString();
}

答案2

得分: 1

处理特定格式的字符串最好使用正则表达式。您定义一个指定的模式,然后在找到与您的模式匹配的部分之后,可以替换它或进一步分析它。

最好编写代码,使其易于扩展。例如,如果添加了新的联系表单(家庭地址、传真、业务电话号码),应该很容易在代码中处理它。您的解决方案使解决此类问题变得更加困难,因为需要添加一个全新的if分支,容易出错,还会降低代码的可读性。

在处理类似字典的数据时(比如您的输入字符串数组),值得使用Map,因为它可以加速处理过程并提高代码的可读性。当存在常量值时,也值得将它们定义为常量或枚举值。此外,Java允许编写更具功能性和可读性的、函数式风格的代码,而不是嵌套的for-each循环 - 值得使用这些特性(JDK8+)。

请查看下面的代码片段和在GitHub上与我的解决方案进行比较的整个项目及测试 - 您可以在那里查看它或克隆存储库并验证代码:

// 我们可以简单地添加新的联系类型和它们的匹配器,使用下面的常量
private static final Map<Pattern, ContactType> CONTACT_PATTERNS = Map.of(
        Pattern.compile("\\[(\\S+)]"), ContactType.TEL,
        Pattern.compile("\\{(\\S+)}"), ContactType.MAIL
);

@Override
public String replace(String input, String[] dictionary) {
    // 我们将字典映射,以使其更易于使用和更可读(也在调试时)
    Map<ContactType, Map<String, String>> contactTypeToNameToValue =
            Arrays.stream(dictionary)
                  .map(entry -> entry.split(" ")) // 字典条目由空格字符分隔
                  .collect(groupingBy(entry -> ContactType.fromString(entry[0]), // 第一部分是联系类型
                                      toMap(entry -> entry[1], // 第二部分是人的姓名
                                            entry -> entry[2]))); // 第三部分是联系值
    String output = input;
    for (Map.Entry<Pattern, ContactType> entry : CONTACT_PATTERNS.entrySet()) {
        Pattern pattern = entry.getKey();
        ContactType contactType = entry.getValue();
        output = pattern.matcher(output)
                        .replaceAll(matchResult -> {
                            String name = matchResult.group(1);
                            // 我们搜索我们的字典并从中获取值,或者如果没有匹配给定名称的内容,则获取原始值
                            return Optional.ofNullable(contactTypeToNameToValue.get(contactType))
                                           .map(nameToValue -> nameToValue.get(name))
                                           .orElseGet(matchResult::group);
                        });
    }
    return output;
}

public enum ContactType {
    TEL,
    MAIL;

    private static ContactType fromString(String value) {
        return Arrays.stream(values())
                     .filter(enumValue -> enumValue.name().equalsIgnoreCase(value))
                     .findFirst()
                     .orElseThrow(RuntimeException::new);
    }
}
英文:

Handling of strings in a specified format is done best using regular expressions. You define a specified pattern and after you find a part matching your pattern, you can replace it or analyze further.

It's best to write your code to make it easily extensible. For example - if a new contact form is added (home address, fax, business phone number), it should be easy to handle it in the code. Your solution makes it harder to resolve such problems as a whole new if branch is required and it's easy to make a mistake, it also makes the code less readable.

When dealing with a kind of dictionary (like your input String array), it's worth using a Map as it makes the processing faster and the code more readable. When a constant values are present, it's worth to define them too - as constants or enum values. Also - Java allows for writing more functional and more readable, functional-style code instead of nested for-eaches - it's worth using those features (JDK8+).

Please, find the code snippet below and a whole project with tests comparing your solution to mine on GitHub - you can view it there or clone the repository and verify the code yourself:

// we can simply add new contact types and their matchers using the constant below
private static final Map&lt;Pattern, ContactType&gt; CONTACT_PATTERNS = Map.of(
Pattern.compile(&quot;\\[(\\S+)]&quot;), ContactType.TEL,
Pattern.compile(&quot;\\{(\\S+)}&quot;), ContactType.MAIL
);
@Override
public String replace(String input, String[] dictionary) {
// we&#39;re mapping the dictionary to make it easier to use and more readable (also in debugging)
Map&lt;ContactType, Map&lt;String, String&gt;&gt; contactTypeToNameToValue =
Arrays.stream(dictionary)
.map(entry -&gt; entry.split(&quot; &quot;)) // dictionary entry is split by &#39; &#39; character
.collect(groupingBy(entry -&gt; ContactType.fromString(entry[0]), // first split part is the contact type
toMap(entry -&gt; entry[1], // second part is the person&#39;s name
entry -&gt; entry[2]))); // third part is the contact value
String output = input;
for (Map.Entry&lt;Pattern, ContactType&gt; entry : CONTACT_PATTERNS.entrySet()) {
Pattern pattern = entry.getKey();
ContactType contactType = entry.getValue();
output = pattern.matcher(output)
.replaceAll(matchResult -&gt; {
String name = matchResult.group(1);
// we search our dictionary and get value from it or get the original value if nothing matches given name
return Optional.ofNullable(contactTypeToNameToValue.get(contactType))
.map(nameToValue -&gt; nameToValue.get(name))
.orElseGet(matchResult::group);
});
}
return output;
}
public enum ContactType {
TEL,
MAIL;
private static ContactType fromString(String value) {
return Arrays.stream(values())
.filter(enumValue -&gt; enumValue.name().equalsIgnoreCase(value))
.findFirst()
.orElseThrow(RuntimeException::new);
}
}

huangapple
  • 本文由 发表于 2020年8月12日 02:38:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/63364411.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定