gpt4 book ai didi

java - 从 OCR 识别的文本中提取信息

转载 作者:塔克拉玛干 更新时间:2023-11-02 19:56:10 28 4
gpt4 key购买 nike

<分区>

我目前正在尝试开发一个光学字符识别 (OCR) 应用程序,它将名片中识别的数据传递到我的手机联系人数据库中。我已经成功地识别了名片中的数据。

项目流程图:

  1. Main Menu .用户必须单击“拍照”按钮才能使用相机拍照。

2.识别出的名片文字会像“拍照”按钮下的文字一样显示。

  1. 然后用户必须点击“保存到联系人”按钮,将联系人信息保存在电话联系人数据库中。识别文本中的联系人姓名、联系电话和电子邮件地址将被传递到尊重的contact information fields中。在我的手机联系人数据库中

使用 OCR 的识别码(主菜单):

package com.datumdroid.android.ocr.simple;

import android.app.Activity;
import android.content.Intent;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import com.googlecode.tesseract.android.TessBaseAPI;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class SimpleAndroidOCRActivity extends Activity {
public static final String PACKAGE_NAME = "com.datumdroid.android.ocr.simple";
public static final String DATA_PATH = Environment
.getExternalStorageDirectory().toString() + "/SimpleAndroidOCR/";

// You should have the trained data file in assets folder

public static final String lang = "eng";

private static final String TAG = "SimpleAndroidOCR.java";

protected Button _button;
// protected ImageView _image;
protected EditText _field;
protected String _path;
protected boolean _taken;

protected static final String PHOTO_TAKEN = "photo_taken";

Button button2; //step 1


@
Override
public void onCreate(Bundle savedInstanceState) {

String[] paths = new String[] {
DATA_PATH, DATA_PATH + "tessdata/"
};

for (String path: paths) {
File dir = new File(path);
if (!dir.exists()) {
if (!dir.mkdirs()) {
Log.v(TAG, "ERROR: Creation of directory " + path + " on sdcard failed");
return;
} else {
Log.v(TAG, "Created directory " + path + " on sdcard");
}
}

}

// lang.traineddata file with the app (in assets folder)


if (!(new File(DATA_PATH + "tessdata/" + lang + ".traineddata")).exists()) {
try {

AssetManager assetManager = getAssets();
InputStream in = assetManager.open("tessdata/" + lang + ".traineddata");
//GZIPInputStream gin = new GZIPInputStream(in);
OutputStream out = new FileOutputStream(DATA_PATH + "tessdata/" + lang + ".traineddata");

// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
//while ((lenf = gin.read(buff)) > 0) {
while ((len = in .read(buf)) > 0) {
out.write(buf, 0, len);
} in .close();
//gin.close();
out.close();

Log.v(TAG, "Copied " + lang + " traineddata");
} catch (IOException e) {
Log.e(TAG, "Was unable to copy " + lang + " traineddata " + e.toString());
}
}

super.onCreate(savedInstanceState);

setContentView(R.layout.main);
button2 = (Button) findViewById(R.id.button2); //step 2


// _image = (ImageView) findViewById(R.id.image);
_field = (EditText) findViewById(R.id.field);
_button = (Button) findViewById(R.id.button);
_button.setOnClickListener(new ButtonClickHandler());

_path = DATA_PATH + "/ocr.jpg";
}
public void onClickbutton2(View v) //step 3
{
startActivity(new Intent("android.intent.action.BUTTON2"));
}



public class ButtonClickHandler implements View.OnClickListener {
public void onClick(View view) {
Log.v(TAG, "Starting Camera app");
startCameraActivity();
}
}

// Simple android photo capture:
// http://labs.makemachine.net/2010/03/simple-android-photo-capture/

protected void startCameraActivity() {
File file = new File(_path);
Uri outputFileUri = Uri.fromFile(file);

final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);

startActivityForResult(intent, 0);
}

@
Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {

Log.i(TAG, "resultCode: " + resultCode);

if (resultCode == -1) {
onPhotoTaken();
} else {
Log.v(TAG, "User cancelled");
}
}

@
Override
protected void onSaveInstanceState(Bundle outState) {
outState.putBoolean(SimpleAndroidOCRActivity.PHOTO_TAKEN, _taken);
}

@
Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
Log.i(TAG, "onRestoreInstanceState()");
if (savedInstanceState.getBoolean(SimpleAndroidOCRActivity.PHOTO_TAKEN)) {
onPhotoTaken();
}
}

protected void onPhotoTaken() {
_taken = true;

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;

Bitmap bitmap = BitmapFactory.decodeFile(_path, options);

try {
ExifInterface exif = new ExifInterface(_path);
int exifOrientation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);

Log.v(TAG, "Orient: " + exifOrientation);

int rotate = 0;

switch (exifOrientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
rotate = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotate = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
rotate = 270;
break;
}

Log.v(TAG, "Rotation: " + rotate);

if (rotate != 0) {

// Getting width & height of the given image.
int w = bitmap.getWidth();
int h = bitmap.getHeight();

// Setting pre rotate
Matrix mtx = new Matrix();
mtx.preRotate(rotate);

// Rotating Bitmap
bitmap = Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, false);
}

// Convert to ARGB_8888, required by tess
bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);

} catch (IOException e) {
Log.e(TAG, "Couldn't correct orientation: " + e.toString());
}

// _image.setImageBitmap( bitmap );

Log.v(TAG, "Before baseApi");

TessBaseAPI baseApi = new TessBaseAPI();
baseApi.setDebug(true);
baseApi.init(DATA_PATH, lang);
baseApi.setImage(bitmap);

String recognizedText = baseApi.getUTF8Text();

baseApi.end();

// You now have the text in recognizedText var, you can do anything with it.
// We will display a stripped out trimmed alpha-numeric version of it (if lang is eng)
// so that garbage doesn't make it to the display.

Log.v(TAG, "OCRED TEXT: " + recognizedText);

if (lang.equalsIgnoreCase("eng")) {
recognizedText = recognizedText.replaceAll("[^a-zA-Z0-9]+", " ");
}

recognizedText = recognizedText.trim();

if (recognizedText.length() != 0) {
_field.setText(_field.getText().toString().length() == 0 ? recognizedText : _field.getText() + " " + recognizedText);
_field.setSelection(_field.getText().toString().length());
}

}


}

添加联系人信息字段:

package com.datumdroid.android.ocr.simple;


import android.content.ContentProviderOperation;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.Photo;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.RawContacts;
import android.provider.MediaStore;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;


public class Contacts extends AppCompatActivity {

EditText edtTxtContactName, edtTxtContactNumber, edtTxtContactEmail;
ImageView imgContactPhoto;
Button btnAddContact;
View view;
String imagePath = null;
Uri uri;
ExifInterface exif;
boolean isButtonClicked = false;
View snackView;
Bitmap rotateBitmap;
InputMethodManager inm;
public static boolean isUpdate;
String cID, cName, cNumber, cEmail; //
Bitmap cPhoto;
public boolean FLAG = true;
Button button3;

@
Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.add_contact_activity_main);

view = findViewById(R.id.rootView);

edtTxtContactName = (EditText) findViewById(R.id.edtTxtContactName);
edtTxtContactNumber = (EditText) findViewById(R.id.edtTxtContactNumber);
edtTxtContactEmail = (EditText) findViewById(R.id.edtTxtContactEmail);

imgContactPhoto = (ImageView) findViewById(R.id.imgContactPhoto);

btnAddContact = (Button) findViewById(R.id.btnAddContact);
inm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);

button3 = (Button) findViewById(R.id.button3);

if (isUpdate) {
cID = getIntent().getExtras().getString("cID");
cName = getIntent().getExtras().getString("cName");

cNumber = getIntent().getExtras().getString("cNumber"); //
cEmail = getIntent().getExtras().getString("cEmail");
cPhoto = getIntent().getParcelableExtra("cPhoto");
btnAddContact.setText(getString(R.string.update_contact));
edtTxtContactName.setText(cName);
edtTxtContactNumber.setText(cNumber); //
edtTxtContactEmail.setText(cEmail);
imgContactPhoto.setImageBitmap(cPhoto);
FLAG = false;

} else {
btnAddContact.setText(getString(R.string.add_contact));
}
btnAddContact.setOnClickListener(new View.OnClickListener() {@
Override
public void onClick(View v) {

if (btnAddContact.getText().toString().equals(getString(R.string.add_contact))) {

if (edtTxtContactName.getText().toString().trim().isEmpty()) {
Snackbar.make(v, "You must provide name.", Snackbar.LENGTH_INDEFINITE).setAction("OK", new View.OnClickListener() {@
Override
public void onClick(View v) {

}
}).show();
} else if (edtTxtContactNumber.getText().toString().trim().isEmpty()) {
Snackbar.make(v, "You must provide number.", Snackbar.LENGTH_INDEFINITE).setAction("OK", new View.OnClickListener() {@
Override
public void onClick(View v) {

}
}).show();
} else {
isButtonClicked = true;
snackView = v;
inm.hideSoftInputFromWindow(v.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
addContact();
}
} else if (btnAddContact.getText().toString().equals(getString(R.string.update_contact))) {
String strName = edtTxtContactName.getText().toString();
String strNumber = edtTxtContactNumber.getText().toString();
String strEmail = edtTxtContactEmail.getText().toString();
imgContactPhoto.setDrawingCacheEnabled(true);
Bitmap bitmap = imgContactPhoto.getDrawingCache();

if (updateContact(cID, strName, strNumber, strEmail, bitmap)) {
Snackbar.make(v, "Contact updated successfully.", Snackbar.LENGTH_LONG).show();
} else {
Snackbar.make(v, "Failed to update contact.", Snackbar.LENGTH_LONG).show();
}

} else {
Snackbar.make(v, "Some internal error occured, Please try after some time.", Snackbar.LENGTH_LONG).show();
}

}
});

imgContactPhoto.setOnClickListener(new View.OnClickListener() {@
Override
public void onClick(View v) {
selectImage();
}
});

}
public void onClickbutton3(View v) //step 3
{
startActivity(new Intent("android.intent.action.BUTTON3"));
}

private void selectImage() {
final CharSequence[] option = {
"Take Photo", "Choose from Gallery"
};
AlertDialog.Builder builder = new AlertDialog.Builder(
Contacts.this);
builder.setTitle("Add Photo!");
builder.setNegativeButton("Cancel",
new DialogInterface.OnClickListener() {@
Override
public void onClick(DialogInterface dialog, int which) {}
});
builder.setItems(option, new DialogInterface.OnClickListener() {

@
Override
public void onClick(DialogInterface dialog, int item) {

if (option[item].equals("Take Photo")) {
clickPhotoFromCamera();
} else if (option[item].equals("Choose from Gallery")) {
uploadPhotoFromCamera();
}
}

});
builder.show();
}

private void clickPhotoFromCamera() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + ".jpg";
File imageStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), imageFileName);
uri = Uri.fromFile(imageStorageDir);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
startActivityForResult(intent, 1);

}

private void uploadPhotoFromCamera() {

Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
intent.setType("image/*");
startActivityForResult(intent, 2);
}

@
Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
if (requestCode == 1) {
imagePath = uri.getPath();
displayImageBitmap(imagePath);
} else if (requestCode == 2) {
Uri selectedImage = data.getData();
String[] filePath = {
MediaStore.Images.Media.DATA
};
Cursor c = getContentResolver().query(selectedImage, filePath,
null, null, null);
c.moveToFirst();
int columnIndex = c.getColumnIndex(filePath[0]);
String picturePath = c.getString(columnIndex);
c.close();
displayImageBitmap(picturePath);
}
}
}

public void displayImageBitmap(String image_path) {
File mediaFile = new File(image_path);
Bitmap myBitmap = BitmapFactory.decodeFile(mediaFile.getAbsolutePath());
int height = (myBitmap.getHeight() * 512 / myBitmap.getWidth());
Bitmap scale = Bitmap.createScaledBitmap(myBitmap, 512, height, true);
int rotate = 0;
try {
exif = new ExifInterface(mediaFile.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
switch (orientation) {
case ExifInterface.ORIENTATION_NORMAL:
rotate = 0;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
rotate = 270;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotate = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_90:
rotate = 90;
break;
}

Matrix matrix = new Matrix();
matrix.postRotate(rotate);
rotateBitmap = Bitmap.createBitmap(scale, 0, 0, scale.getWidth(), scale.getHeight(), matrix, true);
imgContactPhoto.setImageBitmap(rotateBitmap);
}

public void addContact() {
ArrayList < ContentProviderOperation > insertOperation = new ArrayList < ContentProviderOperation > ();
int rawContactID = insertOperation.size();

// Adding insert operation to operations list
// For insert a new raw contact in the ContactsContract.RawContacts
insertOperation.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null)
.withValue(RawContacts.ACCOUNT_NAME, null)
.build());
ByteArrayOutputStream stream = new ByteArrayOutputStream();
if (rotateBitmap != null) { // If an image is selected successfully
rotateBitmap.compress(Bitmap.CompressFormat.PNG, 75, stream);

// For insert Photo in the ContactsContract.Data
insertOperation.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, rawContactID)
.withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1)
.withValue(ContactsContract.Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, stream.toByteArray())
.build());

try {
stream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
// For insert display name in the ContactsContract.Data
insertOperation.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, rawContactID)
.withValue(ContactsContract.Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
.withValue(StructuredName.DISPLAY_NAME, edtTxtContactName.getText().toString())
.build());
// For insert Mobile Number in the ContactsContract.Data
insertOperation.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, rawContactID)
.withValue(ContactsContract.Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
.withValue(Phone.NUMBER, edtTxtContactNumber.getText().toString())
.withValue(Phone.TYPE, Phone.TYPE_MOBILE)
.build());
// For insert Work Email in the ContactsContract.Data
insertOperation.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, rawContactID)
.withValue(ContactsContract.Data.MIMETYPE, Email.CONTENT_ITEM_TYPE)
.withValue(Email.ADDRESS, edtTxtContactEmail.getText().toString())
.withValue(Email.TYPE, Email.TYPE_WORK)
.build());
try {
// Executing all the insert operations as a single database transaction
getContentResolver().applyBatch(ContactsContract.AUTHORITY, insertOperation);
if (isButtonClicked == true) {
edtTxtContactName.setText("");
edtTxtContactNumber.setText("");
edtTxtContactEmail.setText("");
imgContactPhoto.setImageResource(R.drawable.default_contact);
Snackbar.make(snackView, "Contact added successfully", Snackbar.LENGTH_INDEFINITE).setAction("Hide", new View.OnClickListener() {@
Override
public void onClick(View v) {

}
}).show();
} else {
Toast.makeText(getBaseContext(), "Contact is successfully added", Toast.LENGTH_SHORT).show();
finish();
}

} catch (RemoteException e) {
e.printStackTrace();
} catch (OperationApplicationException e) {
e.printStackTrace();
}
}

boolean updateContact(String contactID, String contactName, String contactNumber, String contactEmailAdd, Bitmap bitmap) {
ArrayList < ContentProviderOperation > ops = new ArrayList < > ();
ops.add(ContentProviderOperation
.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.Data.CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=?", new String[] {
contactID, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE
})
.withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, contactName)
.build());

ops.add(ContentProviderOperation
.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.Data.CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=? AND " + ContactsContract.CommonDataKinds.Organization.TYPE + "=?", new String[] {
contactID, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE, String.valueOf(ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE)
})
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, contactNumber)
.build());
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.Data.CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "=? AND " + ContactsContract.CommonDataKinds.Organization.TYPE + "=?", new String[] {
contactID, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE, String.valueOf(Email.TYPE_WORK)
})
.withValue(Email.ADDRESS, contactEmailAdd)
.build());
try {
ByteArrayOutputStream image = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, image);

ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.Data.CONTACT_ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?", new String[] {
contactID, Photo.CONTENT_ITEM_TYPE
})
.withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1)
.withValue(Photo.PHOTO, image.toByteArray())
.build());

/*Builder builder;
builder = ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI);
builder.withSelection(ContactsContract.Data.CONTACT_ID + "=?" + " AND " + ContactsContract.Data.MIMETYPE + "=?",
new String[]{contactID, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE});
builder.withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, image.toByteArray());
ops.add(builder.build());*/
} catch (Exception e) {
e.printStackTrace();
}
try {
getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}

@
Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.add_contact_menu, menu);

MenuItem item = menu.findItem(R.id.action_save);
if (FLAG) {
item.setVisible(true);
this.invalidateOptionsMenu();
} else {
item.setVisible(false);
this.invalidateOptionsMenu();
}
return true;
}

@
Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
switch (id) {
case android.R.id.home:
onBackPressed();
return true;

}
if (id == R.id.action_save) {

if (edtTxtContactName.getText().toString().trim().isEmpty()) {
Toast.makeText(getApplicationContext(), "You must provide name.", Toast.LENGTH_LONG).show();

} else if (edtTxtContactNumber.getText().toString().trim().isEmpty()) {
Toast.makeText(getApplicationContext(), "You must provide number.", Toast.LENGTH_LONG).show();

} else {
inm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
isButtonClicked = false;
addContact();
}

return true;
}
return super.onOptionsItemSelected(item);
}

@
Override
public void onBackPressed() {
super.onBackPressed();
}
}

问题是我不知道如何根据姓名、联系电话、电子邮件地址将每个文本与已识别的文本分开,并将它们传递到我的联系人数据库中的那些字段中。

名片结构/格式不具体,有点难以假设。但是很少有假设:

  1. “@”包含的字符串主要是电子邮件 ID。

  2. 所有带大括号或 + 号的数字大多是电话号码。

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com