字符串处理:分割分隔符数据

5

我将需要从以星号分隔的数据中分离一些信息。

数据格式为:

NAME*ADRESS LINE1*ADDRESS LINE2

规则:

1. Name should be always present
2. Address Line 1 and 2 might not be
3. There should be always three asterisks.

示例:

MR JONES A ORTEGA*ADDRESS 1*ADDRESS2*

Name: MR JONES A ORTEGA
Address Line1: ADDRESS 1
Address Line2: ADDRESS 2

A PAUL*ADDR1**
Name: A PAUL
Address Line1: ADDR1
Address Line2: Not Given

我的算法是:

1. Iterate through the characters in the line
2. Store all chars in a temp variables until first * is found. Reject the data if no char is found before first occurence of asterisk. If some chars found, use it as the name.
3. Same as step 2 for finding address line 1 and 2 except that this won't reject the data if no char is found

我的算法看起来很丑陋。代码看起来更加丑陋。使用//*进行分割也不起作用,因为如果数据是*Address 1*Address2,名称可以被地址行1替换。有什么建议吗?

编辑:

尝试使用不带引号的数据“-MS DEBBIE GREEN * 1036 PINEWOOD CRES **”


请问您能否澄清一下为什么不能使用split函数?我不太明白“name可以被address替换”的部分。 - bezmax
如果数据是地址1地址2,split将在数组中创建两个索引,其中索引0将具有地址1的值,索引2将具有地址2的值。我如何验证名称?没有名字! - Milli Szabo
我在下面的回答中解释了你错在哪里。 - bezmax
5个回答

2
您可以按照以下方式使用String[] split(String regex, int limit)进行操作:
    String[] tests = {
        "NAME*ADRESS LINE1*ADDRESS LINE2*",
        "NAME*ADRESS LINE1**",
        "NAME**ADDRESS LINE2*",
        "NAME***",
        "*ADDRESS LINE1*ADDRESS LINE2*",
        "*ADDRESS LINE1**",
        "**ADDRESS LINE2*",
        "***",
        "-MS DEBBIE GREEN*1036 PINEWOOD CRES**",
    };
    for (String test : tests) {
        test = test.substring(0, test.length() - 1);
        String[] parts = test.split("\\*", 3);
        System.out.printf(
            "%s%n  Name: %s%n  Address Line1: %s%n  Address Line2: %s%n%n",
            test, parts[0], parts[1], parts[2]
        );
    }

这段代码的输出结果为 (在ideone.com中可见):

NAME*ADRESS LINE1*ADDRESS LINE2*
  Name: NAME
  Address Line1: ADRESS LINE1
  Address Line2: ADDRESS LINE2

NAME*ADRESS LINE1**
  Name: NAME
  Address Line1: ADRESS LINE1
  Address Line2: 

NAME**ADDRESS LINE2*
  Name: NAME
  Address Line1: 
  Address Line2: ADDRESS LINE2

NAME***
  Name: NAME
  Address Line1: 
  Address Line2: 

*ADDRESS LINE1*ADDRESS LINE2*
  Name: 
  Address Line1: ADDRESS LINE1
  Address Line2: ADDRESS LINE2

*ADDRESS LINE1**
  Name: 
  Address Line1: ADDRESS LINE1
  Address Line2: 

**ADDRESS LINE2*
  Name: 
  Address Line1: 
  Address Line2: ADDRESS LINE2

***
  Name: 
  Address Line1: 
  Address Line2: 

-MS DEBBIE GREEN*1036 PINEWOOD CRES**
  Name: -MS DEBBIE GREEN
  Address Line1: 1036 PINEWOOD CRES
  Address Line2: 
"\\*"的原因是split需要一个正则表达式,而*是一个正则元字符,因为你想要它字面上的意思,所以需要使用\进行转义。由于\本身是Java字符串转义字符,在字符串中要获取\,需要将其加倍。 limit3的原因是你希望数组有3个部分,包括尾随空字符串。没有limitsplit默认会丢弃尾随空字符串。
在执行split之前,最后一个*被手动丢弃了。

尝试使用不带引号的数据“-MS DEBBIE GREEN*1036 PINEWOOD CRES **”。 - Milli Szabo
由于限制为4,对于上述给定的示例,它将创建4个索引。这看起来并不理想,因为我总是不得不忽略最后一个索引。 - Milli Szabo
1
忽略最后一个索引的问题是什么?如果您不想忽略最后一个索引,请尝试使用@andcoz提供的解决方案。然而,性能会更差,因为它使用更复杂的正则表达式,需要更长的编译时间。 - bezmax
@Milli:我已经修改了,现在是“split-3”。 - polygenelubricants

0
String myLine = "name*addr1*addr2*"
String[] parts = myLine.split('\\*',4);
for (String s : parts) {
    System.out.println(s);
}

输出:

name
addr1
addr2
(empty string)

如果你在"**addr2*"上进行分割,你将得到一个包含"","","addr2"的数组。所以我不明白为什么你不能使用分割。

另外,如果你在"***"上进行分割,你将得到一个包含4个空字符串的数组。

这里有一个例子,请尝试运行这段代码:

public void testStrings() {
    String line = "part0***part3*part4****part8*";
    String[] parts = line.split("\\*");
    for (int i=0;i<parts.length;i++) {
        System.out.println(String.format("parts[%d]: '%s'",i, parts[i]));
    }
}

结果将是:

parts[0]: 'part0'
parts[1]: ''
parts[2]: ''
parts[3]: 'part3'
parts[4]: 'part4'
parts[5]: ''
parts[6]: ''
parts[7]: ''
parts[8]: 'part8'

我明白了。限制使得它变得可以接受。 - Milli Szabo
是的,因为如我在此回复顶部所写的,您应该使用:myLine.split('\\*',3) - 3表示有三个部分。 - bezmax
你需要限制为4,否则最后的 * 将被包括在第三部分。 - polygenelubricants
嗯,有趣的行为。如果未指定限制,则会省略所有额外的分隔符。但是,如果指定了限制,则会将所有额外的分隔符放入最后一个元素中。 - bezmax

0
你可以使用正则表达式来实现这个功能。例如:
String myInput="MR JONES A ORTEGA*ADDRESS 1*ADDRESS2*";

Pattern pattern =  Pattern.compile("([^*]+)\\*([^*]*)\\*([^*]*)\\*");
Matcher matcher = pattern.matcher(myInput);

if (matcher.matches()) {
    String myName = matcher.group(1);
    String myAddress1 = matcher.group(2);
    String myAddress2 = matcher.group(3);
    // ...
} else {
    // input does not match the pre-requisites
}

1
如果数据中充满了用星号(*)分隔的信息,那么它不是看起来难以阅读和扭曲吗? - Milli Szabo
我不确定你的问题是关于什么的。咕哝。如果您添加更多字段和验证,正则表达式将变得越来越长,最终会变得更加复杂。如果您添加第四个字段,例如电话号码,则可以添加验证,编写类似于“([^ *] +)\ *([^ *] *)\ *([^ *] *)\ ((+ \ d {2} \ s)\ d +) \ *”。显然,您可以对其进行注释,您可以编写:“([^ *] +)\ *” / 第1个字段:名称,强制性 / +“([^ *] *)\ *” / 第2个字段:地址,可选 / +“([^ *] *)\ *” / * ... * /。 - andcoz

0
一个完整的解决方案,使用扫描器和正则表达式从文件中读取内容:
import java.io.*;
import java.util.Scanner;
import java.util.regex.Pattern;

public class Test {
    public static void main(String[] args) throws FileNotFoundException {
        Scanner s = new Scanner(new File("data.txt"));
        Pattern p = Pattern.compile("([^\\*]+)\\*([^\\*]*)\\*([^\\*]*)\\*");

        while (s.hasNextLine()) {
            if (s.findInLine(p) == null) {
                s.nextLine();
                continue;
            }

            System.out.println("Name: " + s.match().group(1));
            System.out.println("Addr1: " + s.match().group(2));
            System.out.println("Addr2: " + s.match().group(3));
            System.out.println();
        }
    }
}

输入文件:

MR JONES A ORTEGA*ADDRESS 1*ADDRESS2*
A PAUL*ADDR1**
*No name*Addr 2*
My Name*Some Addr*Some more addr*

输出:

Name: MR JONES A ORTEGA
Addr1: ADDRESS 1
Addr2: ADDRESS2

Name: A PAUL
Addr1: ADDR1
Addr2: 

Name: My Name
Addr1: Some Addr
Addr2: Some more addr

请注意,没有名字的行不匹配(根据规则1:名称应始终存在)。如果您仍然想要处理这些行,请将正则表达式中的“+”更改为“*”。
正则表达式“([^\\*] *)\\ *”可以解读为:“除了星号外的任何内容,零次或多次出现,后跟星号。”

1
如果数据中充满了用星号(*)分隔的信息,那么它不是看起来难以阅读和扭曲吗? - Milli Szabo
为什么你使用 "([^\])\" 而不是 "([^])\"? "*" 在方括号内没有特殊含义。 - andcoz

-1

yourString.split("\\*");应该会给你一个包含姓名、地址1和地址2的数组,其中地址1和地址2可以是空字符串。更多信息:这里


如果数据是地址1地址2,您也可以使用split。您将获得一个包含2个项目而不是3个项目的数组。在这种情况下,您知道array [0]包含名称和地址1。 - Daniel Engmann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接