PLY 文件行的更通用的 TryParse()

More generic TryParse() of line of a PLY file

我正在构建一个导入函数以将 PLY File 加载到我的程序中。我不确定的部分是 Face-Parser 的实现。这样的脸可能有两个不同的版本,Face3(连接 3 个顶点)或 Face4(连接 4 个顶点)。在 *.ply 文件中,它们可能如下所示:

面 3:

3 0 1 2 3

面 4:

4 0 1 2 3

我为它们创建了一个class,实现了接口IFace。但是,该接口无法定义方法 TryParse(),因为我希望它是静态的。所以每个 Face classes 都实现了他们自己的 TryParse 方法和一个额外的 TryParse 到 return 一个 IFace 而不是 Face3Face4。从几点来看,这些方法大多是相同的。目前 TryParse 的实现方式(2 种不同的 classes 中的 2 种方法)感觉非常笨拙。有没有更好的方法来解决这个问题?

这就是我目前的使用方式:

foreach (string line in faceLines)
{
    IFace face = new Face3();              // ugly: I need to instanciate the face bevore I can use "out face"
    var segments = lines[i].Split(' ');    // ugly: I'd rather not touch the line at all. TryParse should do everything

    switch (segments[0])                   // ugly! 
    {
        case "3":
            if (Face3.TryParse(lines[i], out face)) faces.Add((Face4)face);             
            break;
        case "4":
            if (Face4.TryParse(lines[i], out face)) faces.Add((Face4)face);    
            break;
    }
}

这里是 IFaceFace3Face4

public interface IFace
{
    string ToString();        
}       

public struct Face3 : IFace
{
    public int V1;
    public int V2;
    public int V3;

    public override string ToString()
    {
        return string.Format("3 {0} {1} {2}", V1, V2, V3);
    }

    internal static bool TryParse(string Input, out IFace Face)
    {
        Face3 face = new Face3();
        bool b = TryParse(Input, out face);

        Face = (IFace)face;
        return b;
    }

    internal static bool TryParse(string Input, out Face3 Face)
    {
        Face = new Face3();

        var args = Input.Split(' ');
        if (args.Length != 4) return false;

        bool success = true;

        success = success && int.TryParse(args[1], out Face.V1);
        success = success && int.TryParse(args[2], out Face.V2);
        success = success && int.TryParse(args[3], out Face.V3);

        if (!success) return false;
        return true;
    }
}       

public struct Face4 : IFace
{
    public int V1;
    public int V2;
    public int V3;
    public int V4;

    public override string ToString()
    {
        return string.Format("4 {0} {1} {2} {3}", V1, V2, V3, V4);
    }

    internal static bool TryParse(string Input, out IFace Face)
    {
        Face4 face = new Face4();
        bool b = TryParse(Input, out face);

        Face = (IFace)face;
        return b;
    }

    internal static bool TryParse(string Input, out Face4 Face)
    {
        Face = new Face4();

        var args = Input.Split(' ');
        if (args.Length != 5) return false;

        bool success = true;

        success = success && int.TryParse(args[1], out Face.V1);
        success = success && int.TryParse(args[2], out Face.V2);
        success = success && int.TryParse(args[3], out Face.V3);
        success = success && int.TryParse(args[4], out Face.V4);

        if (!success) return false;
        return true;
    }
}

更新: 根据@Heslacher 的回答,我实施了一些更改。由于我希望能够直接调用 Face.TryParse 而无需 FaceParser class,我将接口 IFace 更改为抽象 class Face.因此 Face3Face4 现在不再是 struct 而是 class。我对解决方案非常满意。

正在解析人脸:

Face face;
if (Face.TryParse(lines[i], out face))
{
    faces.Add(face);
}

与:

public abstract class Face
{
    public static bool TryParse(string line, out Face face)
    {
        if (Face3.TryParse(line, out face)) { return true; }
        if (Face4.TryParse(line, out face)) { return true; }
        return false;
    }
}

public class Face3 : Face
{
    public int V1;
    public int V2;
    public int V3;

    public override string ToString()
    {
        return string.Format("3 {0} {1} {2}", V1, V2, V3);
    }

    new internal static bool TryParse(string input, out Face face)
    {
        face = null;

        var args = input.Split(' ');
        if (args.Length != 4) return false;

        Face3 parsedFace = new Face3();
        bool success = true;
        success = success && int.TryParse(args[1], out parsedFace.V1);
        success = success && int.TryParse(args[2], out parsedFace.V2);
        success = success && int.TryParse(args[3], out parsedFace.V3);

        if (!success) return false;

        face = parsedFace;
        return true;
    }
}

public class Face4 : Face
{
    public int V1;
    public int V2;
    public int V3;
    public int V4;

    public override string ToString()
    {
        return string.Format("4 {0} {1} {2} {3}", V1, V2, V3, V4);
    }

    new internal static bool TryParse(string input, out Face face)
    {
        face = null;

        var args = input.Split(' ');
        if (args.Length != 5) return false;

        Face4 parsedFace = new Face4();
        bool success = true;
        success = success && int.TryParse(args[1], out parsedFace.V1);
        success = success && int.TryParse(args[2], out parsedFace.V2);
        success = success && int.TryParse(args[3], out parsedFace.V3);
        success = success && int.TryParse(args[4], out parsedFace.V4);

        if (!success) return false;

        face = parsedFace;
        return true;
    }
}

免责声明: 所以这更像是一次代码审查(你已经删除了你的问题 http://codereview.stackexchange.com),但它应该解决你的问题。


基于 nameing guidelines 输入参数应使用 camelCase 大小写命名。


我看不出为什么您为 Face3Face4 重载了 TryParse() 方法。

设置传递的输入参数 face(命名准则)= null 可以在不向参数分配任何新对象的情况下提前 return。

按照您创建新 Face3 的方式,如果 TryParse() returned false,传入的 IFace 也会被初始化。

这个

public struct Face3 : IFace
{
    public int V1;
    public int V2;
    public int V3;

    public override string ToString()
    {
        return string.Format("3 {0} {1} {2}", V1, V2, V3);
    }

    internal static bool TryParse(string input, out IFace face)
    {

        face = null;

        var args = input.Split(' ');
        if (args.Length != 4)
        {
            return false;
        }

        Face3 currentFace = new Face3();
        bool success = true;
        success=success && int.TryParse(args[1], out currentFace.V1);
        success = success && int.TryParse(args[2], out currentFace.V2);
        success = success && int.TryParse(args[3], out currentFace.V3);

        if (!success)
        {
            return false;
        }
        face = currentFace;
        return true;
    }

}  

会正常工作,即使没有这个

IFace face = new Face3();  // ugly: I need to instanciate the face bevore I can use "out face"  

通过添加一个可以是私有的 FaceParser class 和一个像

这样的静态 TryParse() 方法
private class FaceParser
{
    public static bool TryParse(string line, out IFace face)
    {
        if (Face3.TryParse(line, out face)) { return true; }
        if (Face4.TryParse(line, out face)) { return true; }
        return false;
    }
}  

如果它是私有的,则应包含在要添加到面孔的 class 中,您如何解析线条的初始示例可以简化为

foreach (string line in faceLines)
{
    IFace face; 
    if (FaceParser.TryParse(line, out face))
    {
        faces.Add(face);
    }
}

看来你只是把IFace接口当做一个标记接口。向接口添加 ToString() 方法在某种程度上是多余的,因为每个对象都已经包含一个可覆盖的 ToString() 方法。因此,您可以将界面简化为

public interface IFace {} 

正常 解析器将处理所有标记,从第一个开始,让控制流由读取的标记决定。

在您的情况下,(全局)解析器将读取第一个 int,然后决定它是否继续代码的 "face3" 或 "face4" 部分,在途中读取更多 int 和 return Face3 或行尾的 Face4 实例。

因此,此类解析器的一般概要是:

getNextToken();
switch(currentToken)
{
    case Face3Start:
        getface3();
        break;
    case Face4Start:
        getface4();
        break;
}