天天看點

資料進行中的正則表達

最近在處理雷射雷達點雲資料時,點雲資料在csv檔案中長這個樣子:

資料進行中的正則表達

如果直接用Python處理csv非常友善。通過Pandas很容易處理:

import numpy as np
import pandas as pd
import open3d as o3d


pcd = o3d.geometry.PointCloud()    # pcd類型的資料。

with open("2.csv", encoding="utf-8") as f:
    data = pd.read_csv(f, header=None).values.tolist()
    print(data[:5])
    np_points = np.array(data)[:, 1:4]
    print(np_points.shape)
    pcd.points = o3d.utility.Vector3dVector(np_points)
    o3d.visualization.draw_geometries([pcd])
    # o3d.io.write_point_cloud('E:/project/3.ply', pcd)
           

通過python下pandas處理效率高,速度快,簡單操作。

但是我需要研究學習通過halcon中正則表達實作對csv檔案的正則比對,是以需要看到halcon代碼中的每個變量的變換情況,同時好好學習一下正則表達。

在halcon代碼中:通過檔案讀取之後輸出出來的是tuple的字元數組類型清單構成的向量VecOutLine。把向量轉為tuple元組。如變量監控中的P。生成的字元串。這個字元串就需要我們來正則處理來提取數字,以構成X、Y、Z。

dev_update_off ()

Filename := './0.csv' // 點雲資料的名稱,.txt、.csv、.asc等格式的都可以

NumColumns := 5 //如果點雲的每行資料有3個數字就寫3 (隻有xyz 的資料)

count_seconds (Then)
open_file (Filename, 'input', FileHandle)

VecOutLine.clear()
repeat
    fread_line (FileHandle, VecOutLine.at(VecOutLine.length()), IsEOF)
until (IsEOF)
convert_vector_to_tuple (VecOutLine, P)
S := P[354540]
stop()

P1 := split(P, '\n')
P2 := split(P1, ',')

P3 := number(regexp_replace(P2,'^\\s*(.+?)\\s*\n*$', '$1'))

// tuple_number負責字元轉數字,tuple_string負責數字轉字元。
// tuple_number (P2, P3)

// P4 := number(P2)
// convert_vector_to_tuple({P2},P4)


IndexIsString := find(type_elem(P3),H_TYPE_STRING)
if (IndexIsString > -1)
     throw ('could not convert "' + P3[IndexIsString] + '" to a number')
endif

X := P3[[1:NumColumns:|P3|-1]]
Y := P3[[2:NumColumns:|P3|-1]]
Z := P3[[3:NumColumns:|P3|-1]]
stop()
           
資料進行中的正則表達

我碰到的問題是:

P3 := number(regexp_replace(P2,'^\\s*(.+?)\\s*\n*$', '$1'))
           

 這一行代碼。通過F1查詢知道是替換。

資料進行中的正則表達

通過查詢正則表達文法, 弄明白了,\代表轉義符,比對特殊字元。^和$表示待比對表達式開頭和結尾。()子表達式捕獲分組,分組比對值儲存供以後使用。圓括号中(.+?):

+ 比對前面的子表達式一次或多次。要比對 + 字元,請使用 \+。
. 比對除換行符 \n 之外的任何單字元。要比對 . ,請使用 \. 。
? 比對前面的子表達式零次或一次,或指明一個非貪婪限定符。要比對 ? 字元,請使用 \?。

 .比對除了換行符之外所有字元。這個字元出現一次或多次,然後非貪婪限定,比對一次,滿足條件就停止。這個分組字元串,前面有\\s*不限定個數的空格,後面有\\s*不限定個數的空格,還有一個不限定個數\n*換行符。滿足這個條件用$1來替換,而這個$1替換的是第一個捕獲分組的比對值。

 Replace表達式可以使用标簽'$0'來引用輸入資料中比對的子字元串,'$i'用來引用第i個捕獲組的子比對(對于i <= 9),而'$$'用來引用'$'字面量。
資料進行中的正則表達

注意上面一個細節:轉義字元在一些語言中已經轉移過,如果使用需要多加兩個反斜杠。

 比如在python中,直接如下寫報錯

資料進行中的正則表達

這樣寫才有效。

print(re.findall("^\\s*(.+?)\\s*\\n*$", str3))
           

看看python的處理方式:

import re


f = open("0.csv", encoding="utf-8")
str1 = f.read()
print(type(str1))
print(len(str1))
str2 = str1.split("\n")[:6]
# str2是清單,再轉回string類型。
print(''.join(str2))
str3 = ''.join(str2)

'''
    以上代碼解決了一個open檔案格式為_io.TextIOWrapper轉化為string類型。
    因為太長,而且,列印的時候“\n”自動換行了,是以我這裡先切割,然後取前十段。來解決halcon中正則比對問題。
    同時解決了,list和string類型之間的互轉。
    還有一種處理方式,直接str1= f.read(),不切割。看看效果。應為str1也是string類型。
'''
print(re.match("(.+?)", str3).span())
print(re.findall("^\\s*(.+?)\\s*\\n*$", str3))
# str4 = str3.replace()   # 可以去替換
str4 = re.sub(r"^\\s*(.+?)\\s*$", "$1", str3)
print(str4)
# 去掉空格:
strs = re.sub("\\s", "", str3)
print(strs)


str5 = re.findall("\\-?\\d+", strs)
print(str5)
num_list = list(map(int, str5))
print(num_list)
x = num_list[1::5]
print(x)
y = num_list[2::5]
z = num_list[3::5]
print(y)
print(z)
           

輸出:

<class 'str'>
27652417
0 , -3248 , 9143 , 193 , 1 ,1 , -3248 , 9089 , 193 , 1 ,2 , -3249 , 9040 , 188 , 1 ,3 , -3250 , 8990 , 184 , 1 ,4 , -3247 , 8929 , 194 , 1 ,5 , -3248 , 8878 , 190 , 1 ,
(0, 1)
['0 , -3248 , 9143 , 193 , 1 ,1 , -3248 , 9089 , 193 , 1 ,2 , -3249 , 9040 , 188 , 1 ,3 , -3250 , 8990 , 184 , 1 ,4 , -3247 , 8929 , 194 , 1 ,5 , -3248 , 8878 , 190 , 1 ,']
0 , -3248 , 9143 , 193 , 1 ,1 , -3248 , 9089 , 193 , 1 ,2 , -3249 , 9040 , 188 , 1 ,3 , -3250 , 8990 , 184 , 1 ,4 , -3247 , 8929 , 194 , 1 ,5 , -3248 , 8878 , 190 , 1 ,
0,-3248,9143,193,1,1,-3248,9089,193,1,2,-3249,9040,188,1,3,-3250,8990,184,1,4,-3247,8929,194,1,5,-3248,8878,190,1,
['0', '-3248', '9143', '193', '1', '1', '-3248', '9089', '193', '1', '2', '-3249', '9040', '188', '1', '3', '-3250', '8990', '184', '1', '4', '-3247', '8929', '194', '1', '5', '-3248', '8878', '190', '1']
[0, -3248, 9143, 193, 1, 1, -3248, 9089, 193, 1, 2, -3249, 9040, 188, 1, 3, -3250, 8990, 184, 1, 4, -3247, 8929, 194, 1, 5, -3248, 8878, 190, 1]
[-3248, -3248, -3249, -3250, -3247, -3248]
[9143, 9089, 9040, 8990, 8929, 8878]
[193, 193, 188, 184, 194, 190]
           

提示:在編寫正則時,經驗之談(不明其理):在正規表達式之前加上小寫的r來編譯字元串,就像在C#中編寫路徑用@一樣。

看來還是pandas簡單些。

回歸halcon

在halcon中給了一個非常好的案例:tuple_regexp.hdev

* This program demonstrates the tuple operators and functions for working
* with regular expressions.
* 
* ***************************************************
* ***** Regular expression basics
* ***************************************************
tuple_regexp_match ('abba', 'ab*', Matches)   * *代表前面一個字元出現0次或多次。輸出傳回為‘abb’
tuple_regexp_match ('abba', 'ba*', Matches)    * 傳回為‘b’
tuple_regexp_match ('abba', 'b+a*', Matches)   * 傳回值為‘bba’
tuple_regexp_test ('ababab', '(ab){3}', NumMatches)   * 以下三行傳回值為'110'。
tuple_regexp_test ('abababa', '(ab){3}', NumMatches)
tuple_regexp_test ('abababa', '^(ab){3}$', NumMatches)   *開頭符合,但是結尾不符合。
tuple_regexp_replace ('abba', 'b*', 'x', Result)     * 傳回'xabba',隻比對一次,沒有b也比對。
tuple_regexp_replace ('abba', 'b', 'x', Result)      * 傳回'axba' ,隻比對一次,出現b就替換。
tuple_regexp_replace ('abba', ['b','replace_all'], 'x', Result)   * 傳回 ‘axxa’
* ***************************************************
* ***** Some sample expressions
* ***************************************************
tuple_regexp_replace (['SN/1234567-X','SN/2345678-Y','SN/3456789-Z'], 'SN/(\\d{7})-([A-Z])', 'Product Model $2, Serial Number $1', Result)
tuple_regexp_replace (['01/04/2000','06/30/2007'], '(\\d{2})/(\\d{2})/(\\d{4})', 'Day: $2, Month: $1, Year: $3', Result)
* ***************************************************
* ***** Working with file names
* ***************************************************
get_system ('image_dir', HalconImages)
get_system ('operating_system', OS)
if (OS{0:2} == 'Win')
    tuple_split (HalconImages, ';', HalconImagesSplit)
else
    tuple_split (HalconImages, ':', HalconImagesSplit)
endif
list_files (HalconImagesSplit[0], ['files','follow_links'], Files)
* Filter list of files by extension PNG
tuple_regexp_select (Files, '\\.png$', FilesPNG)
* Ignore images sets by removing all files which end with a digit
tuple_regexp_select (FilesPNG, ['\\d\\.png$','invert_match'], FilesNoDigit)
* Extract file names without slashes (strip directory part)
tuple_regexp_match (FilesNoDigit, '[^/\\\\]*.png', ShortNames)
* Transform file names, e.g., for creating processed output files
tuple_regexp_replace (ShortNames, '(.*)\\.png$', 'out_$1.jpg', ConvertedNames)
* Count number of files with multi-word names (name contains hyphen or underscore)
tuple_regexp_test (ShortNames, '_|-', NumCombined)
* ***************************************************
* ***** Using regular expressions in HDevelop expressions
* ***************************************************
* Again count number of files with digit and calculate percentage
if (|ShortNames| > 0)
    Result := 100.0 * regexp_test(ShortNames,'\\d') / |ShortNames| + '% of PNG file names contain a digit'
endif
* Return letters 2-n of all files starting with 'a'
Result := regexp_match(regexp_select(ShortNames,'^a'),'^a(.*)')
* The operator =~ is short for regexp_test and useful for boolean expressions
if (ShortNames =~ '^z')
    Result := 'A filename starting with z exists'
endif
           

逐行研究一下:看代碼中的注釋。

現在重點看19和20行。

tuple_regexp_replace (['SN/1234567-X','SN/2345678-Y','SN/3456789-Z'], 'SN/(\\d{7})-([A-Z])', 'Product Model $2, Serial Number $1', Result)
           

通過圓括号()把比對拆分成兩部分,待比對規則是以‘SN/’開頭的,第一個捕獲分組是七個數字,第二個捕獲分組是A到Z,然後替換原則:類似C#中的{$1}占位符,隻不過是從1開始。一步完成了資料提取和替換。

在我另外一篇博文中:3D點雲基礎知識(二)-bilibili視訊資源整理(一)代碼有所講解。此處隻分析正則表達部分。在這篇文章中也有涉及。

繼續閱讀